procps 010114

This commit is contained in:
csmall 2002-02-01 22:47:29 +00:00
commit 03a9b5a30f
90 changed files with 20735 additions and 0 deletions

18
.cvsignore.patch Normal file
View File

@ -0,0 +1,18 @@
diff -Naur procps-2.0.6/.cvsignore procps-2.0.7/.cvsignore
--- procps-2.0.6/.cvsignore Wed Dec 31 19:00:00 1969
+++ procps-2.0.7/.cvsignore Fri Jul 14 16:45:01 2000
@@ -0,0 +1,14 @@
+skill
+kill
+oldps
+uptime
+tload
+free
+w
+top
+vmstat
+watch
+snice
+pgrep
+pkill
+sysctl

81
BUGS Normal file
View File

@ -0,0 +1,81 @@
BUG REPORTS
Please read this file before sending in a bug report or patch.
Also, PLEASE read the documentation first. 90% of the mail I get
complaining about procps is due to the sender not having read the
documentation!
Where to send
=============
Send comments, bug reports, patches, etc., to acahalan@cs.uml.edu
What to send
============
It is much more useful to me if a program really crases to recompile it
with make "CC=gcc -ggdb -O", run it with "gdb prog" and "run" and send
me a stack trace ('bt' command). That said, any bug report is still
better than none.
It might be nice to get rid of miscellaneous compiler warnings, but
don't bend over backwards to do it.
Kernel-Dependent Patches
========================
If you send me patches which are specific to *running* with a particular
kernel version of /proc, please condition them with the runtime determined
variable `linux_version_code' from libproc/kvers.c. It is the same
number as the macro LINUX_VERSION_CODE for which the kernel /proc fs
code was compiled.
A macro is provide in libproc/version.h to construct the code from its
components, e.g.
if (linux_version_code < LINUX_VERSION(1,1,30))
/* tty field is only a minor */
A startup call to set_linux_version may also be necessary.
Of course, if a bug is due to a change in kernel file formats, it would
be best to first try to generalize the parsing, since the code is then
more resilient against future change.
If you send me patches which are specific to *compiling* on a particular
version of Linux include a "#if LINUX_VERSION_CODE > 1*0x10000+3*0x100+54"
markup of the patch so that the package may be compiled with older
kernels as well as the "latest and greatest". LINUX_VERSION_CODE is
#define'd in <linux/version.h>.
Note that you should not make patches specific to *compiling* on a
particular version of Linux unless there is nothing else you can do.
Also unified diffs (diff -u) are my preference, context diffs (diff -c )
are kind of usable, and standard diffs (diff) are more useless than a
generic text description of what you did. Just use
diff -u oldfile newfile
or
diff -Naur old-procps-dir new-procps-dir
to create your diffs and you will make me happy. Also make sure to
include a description of what the diff is for or I'm likely to ignore
it because of general lack of time...
Code Structure
==============
My ultimate goal for this package is to be compilable with any kernel
headers and to be able to run under any kernel's /proc. (Don't bother
telling me that I'm not especially close to my ultimate goal... who
is? :-)
Anyhow, another goal is to encapsulate *all* parsing dependent on /proc
file formats into the libproc library. If the API is general enough
it can hopefully stabilize and then /proc changes might only require
updating libproc.so. Beyond that having the set of utilities be simple
command lines parsers and output formatters and encapsulating all kernel
divergence in libproc is the way to go.
Hence if you are submitting a new program or are fixing an old one, keep
in mind that adding files to libproc which encapsulate such things is
more desirable than patching the actual driver program. (well, except
to move it toward the API of the library).

339
COPYING Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<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., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<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.

481
COPYING.LIB Normal file
View File

@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<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!

73
INSTALL Normal file
View File

@ -0,0 +1,73 @@
INSTALL for procps version 2.0.1
================================
Please read the NEWS and BUGS files, also.
==========================================
Re-compiling the package
========================
You want to examine the first 30 or so lines of the Makefile (up to the
configurability note). The destinations of various things attempt FSSTND
compliance, and are pretty standard. The CC/LD flags and which libraries you
use for curses/termcap are here also. It is all annotated there and in a
readily understood format: directories, program to installdir mapping,
sub-packages to build, and compilation options. A few extra points worth
mentioning are:
o The SUBDIRS variable is essentially just a list of subdirectories to
perform a recursive make or make install in. Right now that's just
ps; the old xproc has been removed because it was entirely redundant.
o There is also an option to build and link against a libproc.so which
reduces 'ps' and 'top' sizes by a large fraction. It is on by default,
so change the value of SHARED if you want.
o 'make libinstall' will install the library and header files into standard
system directories for developers of /proc utilities. There are no
library man pages yet, but the headers are fairly well documented.
o You may need to change the INCDIRS definition if your system is
not very standard. The current definition has been tested to work
on several different systems, though.
Once you are satisfied with the top-level Makefile options (and possibly those
in subdirectories) you compile and install the package like so:
(ignore innocuous `rcsid' defined-but-not-used warnings.)
make distclean # clean-out everything to re-make from scratch
make # takes about 0.75 minutes on a PPro 200 with SCSI
su # for write/chown-perms on sys dirs
make install
ldconfig -v # update ld.so to use new libproc if SHARED=1
exit
make distclean # remove anything that can be rebuilt
Miscellaneous Notes
===================
COMPATIBILITY
This code is intended for use with Linux 2.0.xx and 2.2.xx.
Both libc 5 and libc 6 should work.
PS/TOP WCHAN FIELD
In past releases, in order to get WCHAN output for ps, you had to
have a psdatabase for each zImage kernel that you generated from
the vmlinux unstripped binary with /sbin/psupdate. With this release,
you should use the System.map automatically generated by Linus' makefile.
'ps' and 'top' will first use the contents of an environment variable
"PS_SYSTEM_MAP" and then look for System.map files along
the followin search path:
/boot/System.map-V, /boot/System.map, /lib/modules/V/System.map,
Here 'V' is the output of "uname -r" and looks like "2.2.0".
Don't forget the dash! In those same locations, remove psdatabase* files.
PARTIAL INSTALLATION
If you just want to install one program use the targets
install_progname and install_progname.X (where X is the man section)
instead. E.g.
make install_ps install_ps.1
Likewise for component packages, e.g.
make install_psmisc

220
Makefile Normal file
View File

@ -0,0 +1,220 @@
# Makefile for procps. Chuck Blake.
# Portions of this are highly dependent upon features specific to GNU make
export PREFIX = #proc# prefix for program names
export DESTDIR = /
export MANDIR = /usr/man
export MAN1DIR = $(DESTDIR)$(MANDIR)/man1
export MAN5DIR = $(DESTDIR)$(MANDIR)/man5
export MAN8DIR = $(DESTDIR)$(MANDIR)/man8
export BINDIR = $(DESTDIR)/bin
export SBINDIR = $(DESTDIR)/sbin
export XBINDIR = $(DESTDIR)/usr/X11R6/bin
export USRBINDIR = $(DESTDIR)/usr/bin
export PROCDIR = $(DESTDIR)/usr/bin# /usr/proc/bin for Solaris devotees
export APPLNK = $(DESTDIR)/etc/X11/applnk/Utilities
export OWNERGROUP = --owner 0 --group 0
export INSTALLBIN = install --mode a=rx --strip
export INSTALLSCT = install --mode a=rx
export INSTALLMAN = install --mode a=r
BPROG = kill # -> BINDIR
UPROG = oldps uptime tload free w top vmstat watch skill snice # -> USRBINDIR
PPROG = pgrep pkill# -> PROCDIR
SPROG = sysctl
MAN1 = oldps.1 uptime.1 tload.1 free.1 w.1 top.1 watch.1 skill.1 kill.1 snice.1 pgrep.1 pkill.1
MAN5 = sysctl.conf.5
MAN8 = vmstat.8 sysctl.8
DESKTOP = top.desktop
XSCPT = XConsole # -> XBINDIR
SUBDIRS = ps # sub-packages to build/install
# easy to command-line override
export INCDIRS = -I/usr/include/ncurses -I/usr/X11R6/include
export CC = gcc #-ggdb # this gets compiling and linking :-)
export OPT = -O2
export CFLAGS = -D_GNU_SOURCE $(OPT) -I$(shell pwd) $(INCDIRS) -W -Wall -Wstrict-prototypes -Wshadow -Wcast-align -Wmissing-prototypes
export SHARED = 1# build/install both a static and ELF shared library
export SHLIBDIR = $(DESTDIR)/lib# where to install the shared library
export LDFLAGS = -Wl,-warn-common #-s recommended for ELF systems
#LDFLAGS = -qmagic -s# recommended for a.out systems
#LDFLAGS = -Xlinker -qmagic -s# older a.out systems may need this
#LDFLAGS = -N -s# still older a.out systems use this
#BFD_CAPABLE = -DBFD_CAPABLE
#AOUT_CAPABLE = #-DAOUT_CAPABLE
#ELF_CAPABLE = #-DELF_CAPABLE
#LIBBFD = -lbfd -liberty
LIBCURSES = -lncurses# watch is the only thing that needs this
#LIBCURSES = -lcurses -ltermcap# BSD Curses requires termcap
LIBTERMCAP = -lncurses# provides perfectly good termcap support
#LIBTERMCAP = -ltermcap
EXTRALIBS = # -lshadow
W_SHOWFROM = -DW_SHOWFROM# show remote host users are logged in from.
#----------------------------------------------------#
# End of user-configurable portion of the Makefile. #
# You should not need to modify anything below this. #
#----------------------------------------------------#
BUILD = $(BPROG) $(UPROG) $(PPROG) $(SPROG) $(SUBDIRS) $(DESKTOP)
# BUILD LIBRARIES + PROGRAMS
all: $(BUILD)
# INSTALL PROGRAMS + DOCS
install: $(patsubst %,install_%,$(BUILD) $(XSCPT) $(MAN1) $(MAN5) $(MAN8))
ifeq ($(SHARED),1)
install $(OWNERGROUP) --mode a=rx $(LIB_TGT) $(SHLIBDIR)
endif
# INSTALL LIBRARIES + HEADERS (OPTIONAL)
libinstall:
$(MAKE) -C proc install $(LIBPROCPASS)
clean:
$(RM) -f $(OBJ) $(BPROG) $(UPROG) $(PPROG) $(SPROG)
for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done
distclean: clean
for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done
$(RM) -f $(OBJ) $(BPROG) $(UPROG) $(SPROG) \
proc/.depend
#-----------------------------------------------------#
# End of user-callable make targets. #
# You should not need to read anything below this. #
#-----------------------------------------------------#
.PHONY: all install libinstall clean distclean
.PHONY: $(patsubst %,install_%, $(BPROG) $(UPROG) $(SPROG))
.PHONY: proc ps
.PHONY: $(patsubst %,build_%, proc ps)
.PHONY: $(patsubst %,install_%, proc ps)
VERSION = $(shell awk '/^%define major_version/ { print $$3 }' < procps.spec)
SUBVERSION = $(shell awk '/^%define minor_version/ { print $$3 }' < procps.spec)
MINORVERSION = $(shell awk '/^%define revision/ { print $$3 }' < procps.spec)
# Note: LIBVERSION may be less than $(VERSION).$(SUBVERSION).$(MINORVERSION)
# LIBVERSION is only set to current $(VERSION).$(SUBVERSION).$(MINORVERSION)
# when an incompatible change is made in libproc.
LIBVERSION = 2.0.7
ifdef MINORVERSION
LIBPROCPASS = SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) MINORVERSION=$(MINORVERSION) LIBVERSION=$(LIBVERSION)
else
LIBPROCPASS = SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) LIBVERSION=$(LIBVERSION)
endif
# libproc setup
ifeq ($(SHARED),1)
LIB_TGT = proc/libproc.so.$(LIBVERSION)
else
LIB_TGT = proc/libproc.a
endif
$(LIB_TGT): $(wildcard proc/*.[ch])
$(MAKE) -C proc `basename $(LIB_TGT)` $(LIBPROCPASS)
# component package setup -- the pattern should be obvious: A build rule and
# unified executable+documentation install rule. (An extra makefile rule is
# needed for those packages which use Imake.)
ps: build_ps
build_ps: ; $(MAKE) -C ps
install_ps: ps ; $(MAKE) -C ps install
# executable dependencies
oldps kill skill snice top w uptime tload free vmstat utmp : $(LIB_TGT)
# static pattern build/link rules:
%.o : %.c
$(strip $(CC) $(CFLAGS) -c $^)
oldps w uptime tload free vmstat utmp pgrep: % : %.o
$(strip $(CC) $(LDFLAGS) -o $@ $< $(LIB_TGT) $(EXTRALIBS))
# special instances of link rules (need extra libraries/objects)
top: % : %.o
$(strip $(CC) $(LDFLAGS) -o $@ $^ $(LIB_TGT) $(LIBTERMCAP) $(EXTRALIBS))
watch: % : %.o
$(strip $(CC) $(SLDFLAGS) -o $@ $< $(LIBCURSES) $(EXTRALIBS))
# special instances of compile rules (need extra defines)
w.o: w.c
$(strip $(CC) $(CFLAGS) $(W_SHOWFROM) -c $<)
top.o: top.c
$(strip $(CC) $(CFLAGS) -fwritable-strings -c $<)
skill.o: skill.c
$(strip $(CC) $(CFLAGS) -DSYSV -c $<)
snice: skill
ln -f skill snice
kill: skill
ln -f skill kill
pkill: pgrep
ln -f pgrep pkill
# static pattern installation rules
$(patsubst %,install_%,$(BPROG)): install_%: %
$(INSTALLBIN) $< $(BINDIR)/$(PREFIX)$<
$(patsubst %,install_%,$(SPROG)): install_%: %
$(INSTALLBIN) $< $(SBINDIR)/$(PREFIX)$<
$(patsubst %,install_%,$(UPROG)): install_%: %
$(INSTALLBIN) $< $(USRBINDIR)/$(PREFIX)$<
$(patsubst %,install_%,$(PPROG)): install_%: %
$(INSTALLBIN) $< $(PROCDIR)/$(PREFIX)$<
$(patsubst %,install_%,$(XSCPT)): install_%: %
$(INSTALLSCT) $< $(XBINDIR)/$(PREFIX)$<
$(patsubst %,install_%,$(MAN1)) : install_%: %
$(INSTALLMAN) $< $(MAN1DIR)/$(PREFIX)$<
$(patsubst %,install_%,$(MAN5)) : install_%: %
$(INSTALLMAN) $< $(MAN5DIR)/$(PREFIX)$<
$(patsubst %,install_%,$(MAN8)) : install_%: %
$(INSTALLMAN) $< $(MAN8DIR)/$(PREFIX)$<
$(patsubst %,install_%,$(DESKTOP)) : install_%: %
$(INSTALLSCT $< $(APPLNK)/$(PREFIX)$<
# special case install rules
install_snice: snice install_skill
cd $(USRBINDIR) && ln -f skill snice
install_kill: snice install_skill
cd $(USRBINDIR) && ln -f skill kill
install_pkill: pgrep install_pgrep
cd $(USRBINDIR) && ln -f pgrep pkill
# Find all the source and object files in this directory
SRC = $(sort $(wildcard *.c))
OBJ = $(SRC:.c=.o)
CVSTAG = ps_$(VERSION)_$(SUBVERSION)_$(MINORVERSION)
FILEVERSION = $(VERSION).$(SUBVERSION).$(MINORVERSION)
dist: archive
archive:
@cvs -Q tag -F $(CVSTAG)
@rm -rf /tmp/procps
@cd /tmp; cvs -Q -d $(CVSROOT) export -r$(CVSTAG) procps || echo GRRRrrrrr -- ignore [export aborted]
@mv /tmp/procps /tmp/procps-$(FILEVERSION)
@cd /tmp; tar czSpf procps-$(FILEVERSION).tar.gz procps-$(FILEVERSION)
@cd /tmp; cp procps-$(FILEVERSION)/procps.lsm procps-$(FILEVERSION).lsm
@rm -rf /tmp/procps-$(FILEVERSION)
@echo "The final archive is /tmp/procps-$(FILEVERSION).tar.gz"

645
NEWS Normal file
View File

@ -0,0 +1,645 @@
NEWS: what has changed recently with procps, in reverse cronological order.
Please send bug reports to acahalan@cs.uml.edu
*** THIS FILE DOES NOT INCLUDE RECENT CHANGES ***
NEWS for version 2.0.7 of procps
SMP support has been added to top. This adds one line to the screen
for every processor. There is also an (off by default) field to show
the last processor that a process used that replaced the long-dead
LIB field. Please send feedback, positive and negative, on these
changes to procps-bugs@redhat.com
The libproc soname has changed to 2.0.7 because of a structure
change required to add SMP support.
Two programs inspired by Solaris's /usr/proc/bin collection have
been added: pgrep and pkill. By default, for FSSTND/FHS/LSB compliance,
these are installed in /usr/bin; if you want, you can change PROCDIR
in Makefile to move them. You can also make a symlink from
/usr/proc/bin to /usr/bin if you like.
The Makefiles have been sanitized a bit more; they are now less messy
than they were. There is less duplication between the Makefiles now.
The man pages that use tables have been fixed to work work broken
versions of man.
The old wmconfig file (specific to Red Hat) has been replaced with a
desktop file (common across GNOME and KDE).
sysctl returns an error code in a condition in which it didn't before,
and handles EOF correctly.
top now only loads System.map when it is actually going to make use
of it.
vmstat has its buffer size increased and handles page size dynamically.
w has its year display fixed to show the year 2000 as 00 instead of 0,
and to try harder to find a process to display.
watch has a fix for ncurses in recent versions of glibc, and expands
tabs so that they display correctly.
libproc tries to get the default page size from the system header
files, but still has a fallback. It also has been extended to allow
applications to handle their own error reporting in some important
cases. The pwcache has been expanded in size to correctly handle
user names longer than 8 characters. It has been expanded to
provide the lproc field that shows up as top's LC field. A segfault
when /proc is not mounted was fixed. Missing files will cause
applications to exit with an error code instead of good exit code.
A warning when libproc cannot calculate the HZ value (probably due
to a kernel bug) has been supressed by default because it broke
people's scripts unnecessarily. A 64-bit memory size reading
bug was fixed (/proc/meminfo was read incorrectly).
A couple of error messages in ps had newlines added to them. ps
only opens System.map when it is going to make use of it. The
full-page error message has been replaced with a shorter usage
message; the full-page command summary is available with --help,
and the usage message tells about --help; the full-page summary
is now no longer an error message and so it is sent to stdout
insteada of stderr. Processes with run times longer than a day
now have their runtime displayed correctly. ruser output was fixed.
An attempt was made to support one more piece of BSD syntax in the
command line arguments, where pids can follow options with no
intervening space. The ps man page was made a bit more
internally consistent and typos fixed. Fixed a segfault when the
PS_FORMAT environment variable was set wrong.
NEWS for version 2.0.6 of procps
Support for large-memory systems has been added.
LIBVERSION has been incremented because of an incompatible change
in the libproc interface, necessitated by the large-memory support.
A little more error checking in device idle time calculations.
Fixed an almost-impossible file descriptor leak in libproc that
occasionally showed up in long-running top sessions.
The fix for top no longer depending on NR_TASKS in 2.0.5 was
broken; top would die with more than 204 tasks. This has been
fixed.
NEWS for version 2.0.5 of procps
procps can build against the 2.3.18 kernel source; top no longer
depends on NR_TASKS.
sysctl no longer segfaults with -A; has a few parsing fixes
NEWS for version 2.0.4 of procps
sysctl can save/restore settings using /etc/sysctl.conf file
top has -p option and N and A commands.
vmstat doesn't mind interrupt counter overflow on long-running machines
ps can now sort by PCPU.
NEWS for version 2.0.3 of procps
Time calculations fixed (or at least improved...) for SMP machines.
In particular, hertz is calculated correctly.
ps doesn't mind terminal resizing happening while it runs.
sysctl is a nifty new program! Try it! Carefully! :-)
w PCPU and WHAT output fixed.
top batch mode now works without a tty (for instance, via rsh).
new version of watch has a few new features like --differences.
sessreg removed; it belongs where it has been for a long time, in X.
NEWS for version 2.0.2 of procps
Removed xproc entirely; the only thing left there was XConsole, which
was equivalent to xconsole -file /proc/kmsg. Added an XConsole shell
script which does
exec xconsole -file /proc/kmsg "$@"
so that folks who depend on XConsole won't be left behind. This also
fixes the bug that XConsole effectively removed limitations on which
users were allowed to read /proc/kmsg without root having much choice
in the matter other than to remove XConsole...
Removed unused psupdate code (still available as part of procps-1.2.x
for anyone who wants to play with it).
Removed sessreg, as it is included in XFree86 and there is no reason
for the duplication.
Fixed version number generation so that it happens in one place. I'm
tired of releasing versions that misreport their version number...
NEWS for version 2.0.1 of procps
Reverted my changes that had broken Albert's Unix98 compliance.
Major bugs fixed:
o ps now returns failure for "ps <non-existant-pid>"
o ps h has reverted to old linux behaviour except in BSD personality;
--headers and --no-headers long options have been added
o watch buffer overrun fixed (no, not a security hole).
top has -b and -n options added.
NEWS for version 2.0 of procps
Thanks to Albert Cahalan for his rewrite of ps, and for making
the time for new development of ps that I just didn't have. His
research into how ps worked on lots of different UNIX systems
really made this version much more usable for a lot of people.
Read ALL of Albert's comments regarding his 1.9.0 release. If
you do not, you may be surprised when scripts fail. I've tried
to prepare people for the worst of this by giving out the much-detested
warning: `-' deprecated; use `ps aux', not `ps -aux'
message for roughly a year and a half.
NEWS for version 1.9.0 of procps
The ps command now supports simultaneous BSD and Unix98 syntax.
There have been many other changes in ps. (complete rewrite)
Red Hat users should check /etc/sysconfig/network-scripts/ifdown-ppp
for the bogus command "ps aul" and change it to "ps axl".
Scripts that make assumptions about character position are and
were broken because fields may overflow. Scripts should parse by
whitespace and use -o to get the best results. Command names for
swapped out processes are now shown in square brackets instead of
parentheses, as required by the Unix98 standard. This problem can
be avoided entirely by using a SysV format without -f, using the
BSD "c" option, or using the format specifier "comm" with -o.
Some uses of ps in /etc/rc.d/init.d/functions should probably
eventually be changed to stuff somewhat like the following, which
would eliminate the need for awk:
PS_PERSONALITY=linux dead=`ps -p $pid -o pid=`
PS_PERSONALITY=linux echo -n `ps -C $1 -o pid=`
PS_PERSONALITY=linux pid=`ps -C $1 -o pid=`
(or use the 'start-stop-daemon' program that Debian has)
Obsolete files you may have: /etc/psdatabase, /etc/psdevtab,
~/.psdevtab, /usr/man/man8/psupdate.8 and /usr/sbin/psupdate.
NEWS for version 1.2.9 of procps
psupdate has been REMOVED from the default install. You can put it back
if you want to by removing two comment characters in the Makefile, but
I'm not going to tell you which ones. <RANT>If you can't figure it out,
there's a good chance that it won't build on your machine and I don't
want the bug reports. One of the main reasons I have removed it is that
it is not necessary on a properly-maintained system (that is, System.map
exists and is correct) and 4/5ths of the bug reports I have fielded in
the past are from users who are not able to maintain a working system
complaining that it doesn't work, despite the fact that the INSTALL file
explains how to build it in different ways and how to disable it.</RANT>
I will delete without response all email asking me how to build psupdate.
ps now accepts and ignores the g option for those with fingers tied to
the g key from too much exposure to BSD. :-)
Should build properly on ultrasparc -- two people sent the patch and
the second time I didn't notice that the patch went in reverted. So
I have now reverted the reverted patch to get the right behavior.
More reliable command-line parsing in vmstat has been added. If vmstat
didn't die for you before, you won't notice the difference...
Fixed a bug where arbitrary users might have been able to corrupt the
screen of arbitrary other users running top by creating processes with
weird arguments.
"ps ce" was missing a space character; fixed.
NEWS for version 1.2.8 of procps
procps.spec now uses buildroot, and can be built by non-root users.
This means that the default install targets had to change in the
makefiles so that the default install does not require root privs
otherwise. Also changed all "bin" ownership to "root" ownership,
as kmem-ps has died out and folks won't be automatically doing
"chmod u+s /bin/ps" and making their systems insecure thereby.
XConsole no longer installed setuid root by default. This is safer,
if less convenient. Change it if you want to use it as non-root.
top had disgustingly buggy ^Z handling: you could only suspend once,
and if you had suspended, you could no longer read WCHAN information.
Both bugs are now fixed.
xcpustate removed -- it was a very old, obsolete version.
Get a new version from ftp://ftp.cs.toronto.edu/pub/jdd/xcpustate
Now looks for kernel symbols in loadable modules.
psupdate no longer included in RPM package; the theory is that
systems maintained with RPM probably are reasonably recent and
up-to-date and so psupdate is extraneous on those systems, only
adding an unhelpful dependency on a particular version of
binutils.
top printed two spaces instead of just one between command-line arguments.
NEWS for version 1.2.7 of procps
Security hole in XConsole fixed.
Works better with very long uptimes.
Fixed RPM spec file to have correct libproc soname.
skill -i works.
Knows about more major numbers for smart serial boards.
Fixed some small problems in top.
NEWS for version 1.2.6 of procps
Signal handling in top fixed in several ways. Main effect: suspending
works correctly with glibc now.
Patch from Linus for change in some kernel structures to 64-bit.
This means that libproc has changed soname, as the proc_t data
structure has changed incompatibly.
File descriptor leaks in libproc were fixed.
NEWS for version 1.2.5 of procps
Potential security hole fixed: if there was no /etc/psdevtab on
a system, a user could put a link from /tmp/psdevtab to a file
owned by another user, and when that other user ran ps, the
file it pointed to would be killed. This wouldn't work for
root because root would be able to create /etc/psdevtab first,
so arbitrary system files would not be killable. This was fixed
by avoiding psdevtab files that are symlinks or have link counts
higher than 1, and by not looking for /tmp/psdevtab anymore, as
the reason for it to exist is really obsolete.
w -s output has been fixed. Scripts that depend on w -s output
should be examined. The output was broken enough to warrant this
change; it was extremely buggy.
NEWS for version 1.2.4 of procps
I_WANT_A_BROKEN_PS environment variable turns off usage warning.
"w <username>" sense corrected.
argument order problem fixed.
XConsole doesn't dump core on exit any more (only fclose if the
file is open...)
NEWS for version 1.2.3 of procps
psupdate moved to /usr/sbin, since it is no longer necessary.
Added wmconfig for Red Hat systems (others can use it if they
want; if/when wmconfig becomes standard, I'll move it into the
install target of the makefile).
Ugly hack to get around a problem some people were getting with
a too-small variable.
The usage message no longer recommends a deprecated syntax. :-)
NEWS for version 1.2.2 of procps
Made procps report its version number correctly.
Fixed typo in w.1 man page.
NEWS for version 1.2.1 of procps
New address for bug reports against procps: procps-bugs@redhat.com
(That's not an official service of Red Hat Software, just an alias
for me so that bugs get filed properly where I don't lose them...)
Better memory checks.
Fixed a file descriptor leak.
In top, the space key updates immediately.
Fixed broken signal code in top.
Fixed broken screen size calculations in top.
Fixed broken user count calculations in top.
Fixed broken time difference calculations in w.
Link libproc against libc explicitly for better co-existance with libc.
NEWS for version 1.2 of procps
Original author/maintainer, Michael K. Johnson <johnsonm@redhat.com>,
has taken over maintenance again.
psmisc removed, as it is better maintained separately. xload removed,
as it is better maintained as part of XFree86. mknewpty removed, as
it duplicates /dev/MAKEDEV and MAKEDEV is maintained.
Support for 2.1.x kernels and for glibc 2.0.5.
Lots of documentation updates.
BFD support and shared library enabled by default.
Binaries are no longer included in the archive -- get them from your
favorite Linux distributor.
free's display fixed in several ways: no such thing as cached swap,
buffer+ display was incorrectly calculated, and buffer calculations
didn't include cached memory.
Removed mknewpty, because it wasn't perfect and collides with MAKEDEV.
NEWS for version 1.01 of procps
top bugfix release. Fixes memory leak, extra line after loadavg and broken
no-idle mode. Also be a little more aggressive utmp 'from host' filtering in w.
NEWS for version 1.00 of procps
Fixed the ps -t without a -a segfault bug. Royal screwup on my part.
Updated libproc parsing routine for the new vsize output as of 1.3.91-ish.
Expanded the room for the FLAGS in ps -l due to high order bits now being set.
Added a brand new top to the distribution. Highly run-time configurable. The
old top is available for at least a release or so as "top.old". See the new man
page for details, or just run it and type 'h'. This is courtesy of Helmut
Geyer. Thanks Helmut!
egrep -n '\<(tgetent|cm|top_clrtoeol|top_clrtobot|cl|ho|me|md|mr|tgoto)\>' on
top.c and cleaned up all terminal strings being just dumped to stdout. All such
strings are now tputs'd out with putchar. This should fix a lot of problems
people have been having.
Also cleaned up non-HZ based references to jiffies -> real time conversion. I
may have missed one or two, though...
Cleaned up some Makefile things. make distclean; make should really work now.
removed function pointer warnings in xcpustate.c. .depend is properly removed
as are the imake generated makefiles. Shared lib generation bug for libproc
fixed. In general the build should be a lot cleaner, but may still have a glitch
or two.
Fixed a few (but probably not all) Alpha compatibility bugs dealing with memory
alignment. Please let me know if I missed any, or if you like give me a test
account on some Alpha. I don't have access to any Linux other than x86 which is
notoriously forgiving about unaligned memory accesses. Thanks to Alfred Arnold
(a.arnold@kfa-juelich.de) and Harald Koenig for their help so far with this.
Completely (well almost) re-wrote 'w'. From on by default, J/PCPU display is
accurate to 0.01s. w and top use readproc, so snap.c has gone away completely.
'skill' should actually work now, but you may have to use '-c', '-u', etc. to
actually get it to parse the command line correctly.
*** Significant changes in psdevtab inverse device name resolution
------------------------------------------------------------------
Extended semantics of the -n (numeric) option to output the full device number
in hexadecimal for the tty field. That way if /etc/psdevtab cannot be created
rescanning the /dev directory can be short-circuited. Fixed incorrect file
creation mode for /etc/psdevtab. I just forgot to alter my original 0 mode to
something reasonable like 0664. Added fallback locations for psdevtab. First
it tries /etc/psdevtab, then /tmp/psdevtab and then $HOME/.psdevtab. Also,
decided to go ahead and create the devtab file with regular old write instead of
the rw mmap. I still read it with mmap, but there should be no trouble with
that even in ancient kernels. Also changed the semantics such that if any
devtab file is found, it is assumed to be correct regardless of the relative
timestamps of /dev and the file. Hopefully all this will avoid any unnecessary
slowness. I'm still willing to reactivate the older somewhat broken code to
do the mapping without any file as a fallback if the file doesn't exist.
*** Significant changes in psdatabase/WCHAN inverse name resolution
-------------------------------------------------------------------
libproc reads directly from System.map, so psupdate and /etc/psdatabase are no
longer necessary. Hopefully this will make kernel configuration management
simpler requiring only the zImage and System.map, which being the stripped
(compressed) kernel and the symbol table before stripping are natural
complements. This is probably how things should have been all along even going
back to kmem ps days four years ago. Oh well. This change should make dealing
with x86, AXP, Sparc, etc binary formats for kernels a lot easier. All that is
required of System.map is that there be exactly 3 space delimited columns:
"address[single space]anything_with_no_spaces[single space]symbol[newline]"
[ Well, ok, the addresses have to be zero padded so that lexicographic order is
the same as numerical order and the addresses all have to be the same ascii
length and I haven't tested to see how resilient it is to bogus internal records
like multiple newlines in a row. Hopefully aren't editting and commenting their
System.map files. ;-) This could actually work on "sort < /proc/ksyms" also if
we generalize the behavior to work with either two or three columns. ]
We take advantage of the following files in this order:
PS_SYSTEM_MAP # may only point to System.map, not psdatabase
/boot/System.map-`uname -r`, # Note: this is the preferred path
/boot/System.map
/lib/modules/`uname -r`/System.map
/etc/psdatabase
/boot/psdatabase-`uname -r`,
/boot/psdatabase,
/lib/modules/`uname -r`/psdatabase
The reason for the /lib/modules/*/file location is that I imagine many people
have enough trouble keeping track of kernel version-dependent files in the
filesystem as it is, so I didn't want to invent a new place. I keep things in
/boot myself and don't usually have more than 8 or so kernel versions, so this
works for me. I know not everyone uses modules, but it's just a search path
folks... In case it isn't obvious from all of the above, this means that
psupdate is no longer necessary. I include it in this release and include
recognition of it because I realize that some distributions may have scripts
which depend on the old semantics. I doubt there will ever be a compelling
reason to not include support for generating or reading the old psdatabase
files, but they aren't necessary anymore if you have the System.map files from
the kernel builds.
I'm not 100% sold on the ordering of the search path, but I think it makes
sense. If you have reasons why it should be different I may be convincable.
Also I'm interested in exactly what the output of 'nm' looks like on Alpha,
Sparc, etc., architectures. It should be easy enough to adapt the code if it is
basically the same format of <0 padded hex> ... <symbol>\n.
Thanks to Helmut Geyer for the idea of living off the System.map file natively
and Mike Dean for the idea of using an approximate binary search instead of
padding out the lines of System.map with tons of spaces to get equal record
lengths. The cost of doing it approximately is mild, mostly consisting of lots
of scans to get to the next or last newline and a logarithmically few extra
steps to get to the desired record. Only an order of magnitude guess for the
record length is necessary. The code is all mine, though, so all bugs are due
to me alone.
NEWS for version 0.99a of procps
================================
This is a quick bug-fix release to solve a few thorny problems with my probably
overzealous attempt to use the inline-assembly string.h and a Makefile bug or
two. It also fixes free to +/- the cached column too and makes it ignore the
new extra-pretty non-numeric lines that /proc/meminfo is spitting out. It also
fixes the lack of set_linux_verion() in 'w' that caused no command line to be
displayed. I am still working on a much condensed 'w' that should be a lot
easier to maintain and a readproctree that should be usable by both 'ps',
'pstree', and 'w'.
Apparently memory-mapped files are pretty broken before the early 1.3.X kernels
so I may have to rewrite devname.c to not use MAP_WRITE to create psdevtab. Some
people have been claiming that /dev changes at boot-up in some rc scripts. I'm
not really sure why they would want to do that. Seems kind of paranoid to
continually re-make /dev/log. Anyhow, I'm open to suggestions for psdevtab
behaviors. I've been thinking a /tmp/psdevtab fallback (with a careful world
readable umask to avoid repetition) or maybe a $HOME/.psdevtab fallback too.
NEWS for version 0.99 of procps
===============================
This file is a brief catalog of new features or developments in the package.
For general information about using the programs see their man pages.
NEW PACKAGES OR PROGRAMS
========================
LIBPROC
I've modularized some routines and fixed some long standing bugs. Replaced
the regex() recognition of /proc/PID with a simple check of the first
character of the filename being a digit which should be just as safe.
Added an opendir/readdir/closedir style interface to the process table. The
new interface seems cleaner, more intuitive and generally more applicable
(to me anyway). The only program which uses the new interface is ps. 'w'
will follow soon. 'top' may take a while longer... openproc,readproc, and
closeproc are implemented. I still want to do readproctab and rewindproc,
too though.
Added some kernel and package versioning things to make it easier to be run
time adaptable. Also updated sysinfo to understand any /proc/meminfo. A
/proc/stat parser should probably be in there as well with the appropriate
updates to vmstat and xcpustate.
The general direction procps should move in is lightweight command-line or
X11/Motif display/format programs and compartmentalized libproc routines to
parse all of the /proc files. This isolates the utilities from kernel
versioning.
TTY DEVICE NUMBER TO NAME RESOLUTION
Tty device name <-> number mapping has been completely generalized. It now
stat's every character special file in /dev and builds a memory mapped table
of device names indexed in a way that makes lookup of name from number a
fast, constant time process. The extra overhead incurred by building
/etc/psdevtab is non-negligible if you have a large /dev and permissions to
write the file (or its directory) are required to update the file (which is
done if it does not exist or if /dev is newer than /etc/psdevtab).
Hence `root' should `ps' shortly after any modification to /dev (or chmod
666 /etc/psdevtab :-) to avoid ordinary users rebuilding it over and over.
Since such modifications are rare, hopefully having a fallback $HOME
location will not be necessary. If the file is up to date, the overhead
incurred is very small. The generality bought is essentially optimal since
`ps' tailors its notion of name<->devno mapping according to the /dev of the
local system which is the canonical repository of this information.
In principle the name database could encompass all device majors. The file
would be large, but since I use mmap to access it, only the pages with the
major of interest are ever actually read off the disk. Right now I just use
the majors 2,3,4,5,19,20 which should cover both old and new systems with
both master and slave devices (I know... no reason for the masters... :-)
and the multiport serial devices. Also the 'mknewpty' script is provided
to update your /dev directory to the new pty master/slave devices.
The tty abbreviation scheme has been rationalized to match device special
files. The leading "tty" or "cu" is stripped, so cua3 -> a3, tty1 -> 1 and
ttyp9 -> p9. The t flag in ps now works with a full device names and to
pick up processes even if they aren't owned by the owner of ps, e.g.
"ps tcua0" picks up gpm for everyone. This seems desirable.
WATCH
A little program similar to another called 'vis' which simply re-displays
in a polling fashion the output of other programs. "watch ps --sort:utime"
might be dubbed a poor man's 'top'. Though this has been included in procps
for some time it hasn't been built or installed by default. It is now.
SKILL/SNICE
I have written the necessary machine-dependant file for 'skill' and tested
it somewhat under Linux. It seems to mostly work, but there are probably a
few glitches. This is a generalization of the 'killall' concept. You can
send signals or change priority based upon user, command basename (the same
that 'ps c' gives), terminal, etc. If you have a user named 'satan' "skill
-u satan" will kill all their processes. :-) See the man page for more
details.
An annoyance of the current implementation is that although permission to
send signals is based upon the real user id, /proc only gives the effective
uids of processes. Hence processes which you *could* kill because they're
suid-root (X say) won't be detected as kill-able. Either /proc + readproc
need to be updated to report the *real* uid to skil or skill needs to try to
send the signal even if the uid doesn't match.
CHANGES TO OLD PROGRAMS
=======================
MAKEFILE
The directory hierarchy has been restructured. It is now easier to have the
multiple components to the suite under nearly autonomous administration.
The library code has also been moved to a subdirectory. The best thing
about the new setup is that things like Imake generated Makefiles with
preconceived notions of 'make install' can be used without getting into the
business of re-writing component package makefiles.
There is now an option to build a shared libproc.a which reduces 'ps' and
'top' sizes by about 10K apiece. Simply change the value of SHARED. Also,
one may optionally install the library header files and archive/shared
object files into standard system directories. There are no library man
pages yet, but the headers are fairly descriptive.
PS
Several long standing bugs have been fixed and much of the internal code was
re-written to use my new directory-style interface to the process table. In
particular if sorting is disabled (with '-o') the process entries are output
to the terminal as soon as possible (making it more helpful under heavy
system load).
I am considering several new additional features to `ps' including
regex filtering of which processes to list,
"grep -s" silent testing for existing of processes matching criteria,
run-time/user-defined output formats.
I would also like to completely phase the w,top code which uses the snapshot
interface instead of the 'readproc' interface. And of course adding long
options for the rest of the options would be nice too (I may not get around
to doing this anytime really soon so patches which implement any of these
things would most likely be gleefully accepted).
PSUPDATE
psupdate has been updated to work with ELF kernels. If you compile it as
an ELF binary it will handle both a.out and ELF kernels. If you compile it
as an a.out binary it will only handle a.out kernels. Many thanks to Jeff
Uphoff.
psupdate has also been updated to work with any binary type supported
by libbfd. This is the default configuration.
Many thanks to David Mossberger-Tang.
TOP
A user-defined format would be nice here too. Alternate sorting criteria
(top memory users instead of top CPU users, etc.) may be another interesting
alternative. Of course the sorting in ps can do all of that, but it doesn't
have any optimal screen update action going down... :-)

176
TODO Normal file
View File

@ -0,0 +1,176 @@
-------------------------- general ------------------------
Implement /usr/proc/bin tools like Solaris has.
The main Makefile is broken. Try 'touch top.h' to see.
The Makefile is too clever, so it's still not fixed.
Don't these really belong in the procps package?
killall pstree fuser lsof who
(they are maintained elsewhere, which causes version problems)
Cache results of dev_to_tty.
---------------------- kernel -------------------------
Add an "adopted child" flag to mark processes that are not
natural children of init. This can make --forest work better.
Add a thread group ID, to be shared by all tasks that are related by
the clone() system call. This ID might be made unique from boot to
shutdown, perhaps being a 16-bit CPU number and 48-bit per-CPU
serial number.
Make the kernel group /proc listing output by thread group.
Without this, a thread-aware ps must always sort processes.
Supply the task ID (the "PID"/"TID") of the thread group leader.
I define "leader" as the first process of a thread group.
Don't reuse the task ID of a thread group leader until all threads
are dead. Better yet, don't let the leader exit.
Supply better data for top's CPU state display. Currently top has
to subtract old numbers from new numbers and divide that result by
the number of processors. The kernel won't even supply the number
of processors in a portable way.
Mark threads, and supply a list of other threads.
Supply data for the ADDR and JOBC fields.
Support & supply data for SL and RE.
Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is
not accessable.
Put unique ID at the top of System.map and in /proc, to make sure
there is never a mismatch.
Maybe just put the symbols in the kernel like some other systems do.
Raul Miller writes: You might want to implement this as user data which just
happens to come from a wierd source -- instead of just freeing the memory
(like happens with init code), you want it to wind up in the buffer cache.
Add /proc/*/.bindata files to avoid string parsing. It should be an array
of 64-bit values on all machines. New entries go on the end and obsolete
ones get filled in with something logical -- entries must never be deleted!
Add all the stuff Solaris has. This would also replace ptrace.
The kernel should report these task_struct members:
has_cpu
processor
---------------------- w --------------------------
The LOGIN@ column sometimes has a space in it. This makes correct
scripting difficult.
Verify that DNS control does not give a user the power to specify
arbitrary data for the FROM column. (could set root's VGA color map!)
---------------------- vmstat --------------------------
Extract /proc/stat parsing from vmstat into libproc somewhere.
--------------------- libproc ----------------------
Stop storing fields with duplicate info (often different
units: kB and pages, seconds and jiffies) in the proc_t struct.
---------------------- top -------------------------
Share more stuff with ps.
---------------- ps for now, maybe move to libproc ------------------
With forest output and a tty named /dev/this_is_my_tty, the position
of the command name gets messed up. (we print too many spaces) (fixed?)
Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_*_ HP_fl JFMT OL_m
(jobc,cpu,sl,re,cpu,prmgrp,cp,m_size,m_swap,m_share,vm_lib,m_dt)
Note that "cpu" has two meanings.
Add Beowulf support. This is ugly, since the current patches use a
daemon to collect info and add a HOST field after the PID field.
Query optimizer, put cheap/required process selection first.
Avoid reading both /proc/*/status and /proc/*/stat. Actually, avoid
reading lots of stuff! Avoidance got disabled. (at least don't read the
environment!!!)
Maybe ps should put a 'C' in front of fields when they are affected
by cumulative mode. Debian ps did that. (move flag to common.h...)
Support printing the client hostname (the FROM that w(1) uses) in place
of a pty. Maybe do this when PS_PERSONALITY=linux.
Disgusting color support, for completeness. (Debian ps had it)
Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now)
1------8 1--4
ttyS2 S2
ttyI31 I31
pts/7 7 Short form could be /999.
pts/9999 9999 Short form could just be trunctuated to /999.
tty7 7 Short form could be vc-7.
tty63 63 Short form could be vc63.
Internationalization, as specified by XPG3, Volume 1, Commands and Utilities.
(and suggested by Unix98) LC_TIME affects date format.
----------------------- ps -----------------------
Solve the "ps reports wrong date" bug. (recent processes on an SMP system
are reported as being 5 days old, while boot processes look normal)
(reported for procps-2.0.2 in May 1999)
Show real-time priorities. (type & number)
Add an option to select all processes that a user can kill.
(related to RUID, EUID, tty, etc. -- but maybe ignore root power)
Add a nice display option for killing things.
ruser,euser,ppid,pid,pmem,stime,args isn't too bad
Make the column alignment algorithm support this:
FOO BAR
8 44444
453 45
45 2989
63666 0
34 333
(useful for the UNIX tty and time values, since the time might look
like 100-10:40:32 for old processes and the tty might have extra room)
Improve long sort/format specifiers documentation and fill in the missing
code as much as the kernel can support. Make sure that memory amounts are in
pages when they should be and in kB when they should be, not backwards.
output encoding: UTF8 --nul --null
Make BSD formats use non-standard BSD time format, at least when it
doesn't violate the "no whitespace" rule.
Try to make -jl fit in 80 columns. Do we need more than 1000 pty devices,
9 flag bits, etc.? (hmmm, Linux supports 2048 pty devices now, and we
might also want to steal whitespace there when the time column overflows)
When not in strict Unix98 mode, let foo=8 specify that foo is 8
characters wide. Debian did that. Then foo=8=bar and foo=bar=8
could change both header and width.
Better unmangling of '?' as a tty. The shell destroys '?' when there
is a filename that matches. If the argument seems like garbage,
check for a file that might have screwed up the '?'.
If the 'O' option is given something already implied by 'O',
assume the user wanted a sorting option.
Conflict:
Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime
AIX THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm
AIX looks like this:
USER PID PPID TID S C PRI SC WCHAN FLAG TTY BND CMD

5
XConsole Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
# xconsole is capable of doing everything XConsole did, and is maintained...
# it is not setuid root, so we just exit if called as non-root
[ $(id -u) = 0 ] || exit 0
exec xconsole -file /proc/kmsg "$@"

45
free.1 Normal file
View File

@ -0,0 +1,45 @@
.\" -*-Nroff-*-
.\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu.
.\" Freely distributable under the terms of the GPL
.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
.SH NAME
free \- Display amount of free and used memory in the system
.SH SYNOPSIS
.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s"
.I delay
.RB "] [" "\-t" "] [" "\-V" ]
.SH DESCRIPTION
\fBfree\fP displays the total amount of free and used physical and swap
memory in the system, as well as the buffers used by the kernel.
The shared memory column should be ignored; it is obsolete.
.SS Options
The \fB-b\fP switch displays the amount of memory in bytes; the
\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP
switch displays it in megabytes.
.PP
The \fB-t\fP switch displays a line containing the totals.
.PP
The \fB-o\fP switch disables the display of a "buffer adjusted" line. Unless
specified \fBfree\fP subtracts/adds buffer memory from/to the used/free memory
reports (respectively!).
.PP
The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You
may actually specify any floating point number for \fIdelay\fP,
.BR usleep (3)
is used for microsecond resolution delay times.
.PP
The \fB\-V\fP displays version information.
.SH FILES
.ta
.IR /proc/meminfo "\-\- memory information"
.fi
.SH "SEE ALSO"
.BR ps (1),
.BR top(1)
.SH AUTHORS
Written by Brian Edmonds.
Send bug reports to <acahalan@cs.uml.edu>

80
free.c Normal file
View File

@ -0,0 +1,80 @@
/* free.c - a /proc implementation of free */
/* Dec14/92 by Brian Edmonds */
/* Thanks to Rafal Maszkowski for the Total line */
#include "proc/sysinfo.h"
#include "proc/version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#define S(X) ( ((unsigned long long)(X) << 10) >> byteshift)
static int byteshift = 10;
static int total = 0;
int main(int argc, char *argv[]){
int i;
int old_fmt = 0;
int rtime = 0;
/* check startup flags */
while( (i = getopt(argc, argv, "bkmos:tV") ) != -1 )
switch (i) {
case 'b': byteshift = 0; break;
case 'k': byteshift = 10; break;
case 'm': byteshift = 20; break;
case 'o': old_fmt = 1; break;
case 's': rtime = 1000000 * atof(optarg); break;
case 't': total = 1; break;
case 'V': display_version(); exit(0);
default:
fprintf(stderr, "usage: %s [-b|-k|-m] [-o] [-s delay] [-t] [-V]\n", argv[0]);
return 1;
}
do {
meminfo();
printf(" total used free shared buffers cached\n");
printf(
"%-7s %10Ld %10Ld %10Ld %10Ld %10Ld %10Ld\n", "Mem:",
S(kb_main_total),
S(kb_main_used),
S(kb_main_free),
S(kb_main_shared),
S(kb_main_buffers),
S(kb_main_cached)
);
if(!old_fmt){
printf(
"-/+ buffers/cache: %10Ld %10Ld\n",
S(kb_main_used-kb_main_buffers-kb_main_cached),
S(kb_main_free+kb_main_buffers+kb_main_cached)
);
}
printf(
"%-7s %10Ld %10Ld %10Ld\n", "Swap:",
S(kb_swap_total),
S(kb_swap_used),
S(kb_swap_free)
);
if(total == 1){
printf(
"%-7s %10Ld %10Ld %10Ld\n", "Total:",
S(kb_main_total + kb_swap_total),
S(kb_main_used + kb_swap_used),
S(kb_main_free + kb_swap_free)
);
}
if(rtime){
fputc('\n', stdout);
fflush(stdout);
usleep(rtime);
}
} while(rtime);
return 0;
}

216
gfinger.p Normal file
View File

@ -0,0 +1,216 @@
Return-Path: <qpliu@ulch.Princeton.EDU>
Date: Wed, 24 Feb 93 23:37:45 -0500
From: qpliu@ulch.Princeton.EDU (Quowong P Liu)
To: johnsonm@stolaf.edu
Subject: Suggestions for procps
Reply-To: qpliu@princeton.edu
[...]
Since you are collecting programs that use the proc-fs, I have included
some patches to GNU finger that allow it to read the proc information
that it uses.
[...]
Here the diffs for GNU finger begin.
-------------BEGIN---------------
diff -ru finger-1.37.orig/lib/os.c finger-1.37/lib/os.c
--- finger-1.37.orig/lib/os.c Thu Oct 22 17:01:10 1992
+++ finger-1.37/lib/os.c Mon Feb 8 00:56:32 1993
@@ -23,7 +23,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
+#ifndef linux
#include <sys/acct.h>
+#endif
#include <time.h>
#include <packet.h>
@@ -43,9 +45,11 @@
#ifdef HAVE_PROC_FS
#include <sys/signal.h>
+#ifndef linux
#include <sys/fault.h>
#include <sys/syscall.h>
#include <sys/procfs.h>
+#endif /*linux*/
#endif
#include <general.h>
@@ -193,6 +197,7 @@
long idle, current_time, get_last_access ();
char *hostname, *ttyloc, *tty_location ();
#ifdef HAVE_PROC_FS
+#ifndef linux
prpsinfo_t procinfo;
struct procent
{
@@ -202,6 +207,16 @@
char fname[sizeof (procinfo.pr_fname)];
}
*proctable, *tableptr;
+#else
+ struct procent
+ {
+ time_t start;
+ int uid;
+ dev_t tty;
+ char fname[15];
+ }
+ *proctable, *tableptr;
+#endif /* linux */
int nprocs = 0;
#endif /* HAVE_PROC_FS */
@@ -235,6 +250,7 @@
searching through user list. */
#ifdef HAVE_PROC_FS
+#ifndef linux
{
extern char *savedir ();
char *dirstring = savedir ("/proc", 2*1024, &nprocs);
@@ -280,6 +296,64 @@
free (dirstring);
}
}
+#else /* linux */
+ {
+
+ extern char *savedir ();
+ char *dirstring = savedir ("/proc", 2*1024, &nprocs);
+ char *proc;
+ int procfd;
+ char procfile[17], procstatbuffer[1024];
+ struct stat proc_fs_stat;
+ char procstate;
+ int tmp_tty; /* dev_t is unsigned, -1 means no controlling tty */
+
+ /* Allocate process table */
+ if (nprocs)
+ proctable = (struct procent *) xmalloc (sizeof *proctable * nprocs);
+
+ /* Walk processes */
+ if (dirstring && nprocs)
+ {
+ for (tableptr = proctable, proc = dirstring;
+ *proc; proc += strlen (proc) + 1)
+ {
+ sprintf (procfile, "/proc/%.5s/stat", proc);
+
+ if ((procfd = open (procfile, O_RDONLY)) >= 0
+ && read (procfd, procstatbuffer, 1024))
+ {
+ /* see /usr/src/linux/fs/proc/array.c
+ * this is for 0.99.4
+ */
+ sscanf (procstatbuffer, "%*d (%14s %c %*d %*d %*d %d "
+ "%*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u "
+ "%*u %hd" /* " %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d "
+ "%*d %*d %*u" */,
+ tableptr->fname, &procstate, &tmp_tty,
+ &tableptr->start);
+ tableptr->fname[strlen(tableptr->fname)-1] = 0; /*trailing )*/
+ tableptr->tty = tmp_tty;
+ if (fstat (procfd, &proc_fs_stat) == 0)
+ tableptr->uid = proc_fs_stat.st_uid;
+
+ /* Only collect non-zombies with controlling tty */
+ if (tmp_tty > 0 && procstate != 'Z')
+ {
+ tableptr++;
+ }
+ else
+ nprocs--;
+
+ close (procfd);
+ }
+ else
+ nprocs--;
+ }
+ free (dirstring);
+ }
+ }
+#endif /* linux */
#endif /* HAVE_PROC_FS */
@@ -332,10 +406,14 @@
if (stat (device_name, &finfo) != -1)
{
+#ifdef linux
+ dev_t tty = finfo.st_rdev&0xff;
+#else
#ifdef HAVE_ST_RDEV
dev_t tty = finfo.st_rdev;
#else
dev_t tty = finfo.st_dev;
+#endif
#endif
int file;
diff -ru finger-1.37.orig/lib/packet.c finger-1.37/lib/packet.c
--- finger-1.37.orig/lib/packet.c Wed Oct 21 18:04:38 1992
+++ finger-1.37/lib/packet.c Mon Feb 8 01:10:00 1993
@@ -348,7 +348,9 @@
perror (filename);
}
+#ifndef linux /* doesn't have fsync (as of .99.4) */
fsync (file);
+#endif
close (file);
}
diff -ru finger-1.37.orig/lib/savedir.c finger-1.37/lib/savedir.c
--- finger-1.37.orig/lib/savedir.c Fri Oct 16 21:52:40 1992
+++ finger-1.37/lib/savedir.c Mon Feb 8 01:06:47 1993
@@ -132,7 +132,7 @@
char *
stpcpy (dest, source)
char *dest;
- char *source;
+ const char *source; /* gcc insists on prototype consistency */
{
while ((*dest++ = *source++) != '\0')
/* Do nothing. */ ;
diff -ru finger-1.37.orig/src/finger.c finger-1.37/src/finger.c
--- finger-1.37.orig/src/finger.c Wed Oct 21 20:41:13 1992
+++ finger-1.37/src/finger.c Mon Feb 8 00:56:32 1993
@@ -192,6 +192,9 @@
long addr;
char *finger_server = NULL;
int suppress_hostname = 0;
+#if defined(linux) /* gethostbyaddr evidently returns pointer to static area */
+ int do_hostfree = 0;
+#endif /* linux */
username = savestring (arg);
@@ -237,6 +240,9 @@
{
host = (struct hostent *) xmalloc (sizeof (struct hostent));
host->h_name = hostname;
+#if defined (linux)
+ do_hostfree = 1;
+#endif /* linux */
}
}
else
@@ -296,6 +302,11 @@
if (finger_server)
free (finger_server);
+#if !defined (linux)
if (host)
free (host);
+#else
+ if (host && do_hostfree)
+ free (host);
+#endif
}
-------------END-------------

32
glibc.patch Normal file
View File

@ -0,0 +1,32 @@
From: Nix <nix@esperi.demon.co.uk>
Date: Tue, 2 Mar 1999 05:25:08 +0000 (GMT)
Subject: Re: [PATCH] Re: that fputs() thing
1999-03-02 Nix <nix@esperi.demon.co.uk>
* stdlib/fputs.c (fputs): Return `len' rather than 0 on success.
*** 2.1/stdio/fputs.c.orig Tue Mar 2 05:17:12 1999
--- 2.1/stdio/fputs.c Tue Mar 2 05:18:07 1999
***************
*** 27,35 ****
{
const size_t len = strlen (s);
if (len == 1)
! return putc (*s, stream) == EOF ? EOF : 0;
if (fwrite ((void *) s, 1, len, stream) != len)
return EOF;
! return 0;
}
weak_alias (fputs, fputs_unlocked)
--- 27,35 ----
{
const size_t len = strlen (s);
if (len == 1)
! return putc (*s, stream) == EOF ? EOF : len;
if (fwrite ((void *) s, 1, len, stream) != len)
return EOF;
! return len;
}
weak_alias (fputs, fputs_unlocked)

96
kill.1 Normal file
View File

@ -0,0 +1,96 @@
,\" t
.\" (The preceding line is a note to broken versions of man to tell
.\" them to pre-process this man page with tbl)
.\" Man page for kill.
.\" Licensed under version 2 of the GNU General Public License.
.\" Written by Albert Cahalan; converted to a man page by
.\" Michael K. Johnson
.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual"
.SH NAME
kill \- report process status
.SH SYNOPSIS
.TS
l l.
kill pid ... Send SIGTERM to every process listed.
kill signal pid ... Send a signal to every process listed.
kill -s signal pid ... Send a signal to every process listed.
kill -l List all signal names.
kill -L List all signal names in a nice table.
kill -l signal Convert a signal number into a name.
.TE
.SH DESCRIPTION
The default signal for kill is TERM. Use -l or -L to list available signals.
Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
.SH SIGNALS
The signals listed below may be available for use with kill.
When known constant, numbers and default behavior are shown.
.TS
lB rB lB lB
lfCW r l l.
Name Num Action Description
.TH
ALRM 14 exit
HUP 1 exit
INT 2 exit
KILL 9 exit this signal may not be blocked
PIPE 13 exit
POLL exit
PROF exit
TERM 15 exit
USR1 exit
USR2 exit
VTALRM exit
STKFLT exit may not be implemented
PWR ignore may exit on some systems
WINCH ignore
CHLD ignore
URG ignore
TSTP stop may interact with the shell
TTIN stop may interact with the shell
TTOU stop may interact with the shell
STOP stop this signal may not be blocked
CONT restart continue if stopped, otherwise ignore
ABRT 6 core
FPE 8 core
ILL 4 core
QUIT 3 core
SEGV 11 core
TRAP 5 core
SYS core may not be implemented
EMT core may not be implemented
BUS core core dump may fail
XCPU core core dump may fail
XFSZ core core dump may fail
.TE
.SH NOTES
Your shell (command line interpreter) may have a built-in kill command.
You may need to run the command described here as /bin/kill to solve
the conflict.
.SH EXAMPLES
kill -9 -1
.br
kill -l 11
.br
kill -L
.br
kill 123 543 2341 3453
.SH "SEE ALSO"
top(1) skill(1) kill(2) renice(1) nice(1)
.SH STANDARDS
This command meets appropriate standards. The -L flag is Linux-specific.
.SH AUTHOR
Albert Cahalan <acahalan@cs.uml.edu> wrote kill in 1999 to replace the
version that was not standards compliant. Michael K. Johnson
<johnsonm@redhat.com> is the current maintainer of the procps collection.
Please send bug reports to <acahalan@cs.uml.edu>

486
minimal.c Normal file
View File

@ -0,0 +1,486 @@
/*
* Copyright 1998 by Albert Cahalan; all rights reserved.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
/* This is a minimal /bin/ps, designed to be smaller than the old ps
* while still supporting some of the more important features of the
* new ps. (for total size, note that this ps does not need libproc)
* It is suitable for Linux-on-a-floppy systems only.
*
* Maintainers: do not compile or install for normal systems.
* Anyone needing this will want to tweak their compiler anyway.
*/
#include <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 <sys/dir.h>
#include <asm/param.h> /* HZ */
#include <asm/page.h> /* PAGE_SIZE */
static int P_euid;
static int P_pid;
static char P_cmd[16];
static char P_state;
static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid;
static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value;
static unsigned long P_start_time, P_vsize;
static long P_rss;
static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
static unsigned long P_wchan, P_nswap, P_cnswap;
#if 0
static int screen_cols = 80;
static int w_count;
#endif
static int want_one_pid;
static const char *want_one_command;
static int select_notty;
static int select_all;
static int ps_format;
static int old_h_option;
/* we only pretend to support this */
static int show_args; /* implicit with -f and all BSD options */
static int bsd_c_option; /* this option overrides the above */
static int ps_argc; /* global argc */
static char **ps_argv; /* global argv */
static int thisarg; /* index into ps_argv */
static char *flagptr; /* current location in ps_argv[thisarg] */
#ifndef PAGE_SIZE
#warning PAGE_SIZE not defined, assuming it is 4096
#define PAGE_SIZE 4096
#endif
#ifndef HZ
#warning HZ not defined, assuming it is 100
#define HZ 100
#endif
static void usage(void){
fprintf(stderr,
"-C select by command name (minimal ps only accepts one)\n"
"-p select by process ID (minimal ps only accepts one)\n"
"-e all processes (same as ax)\n"
"a all processes w/ tty, including other users\n"
"x processes w/o controlling ttys\n"
"-f full format\n"
"-j,j job control format\n"
"v virtual memory format\n"
"-l,l long format\n"
"u user-oriented format\n"
"-o user-defined format (limited support, only \"ps -o pid=\")\n"
"h no header\n"
/*
"-A all processes (same as ax)\n"
"c true command name\n"
"-w,w wide output\n"
*/
);
exit(1);
}
/*
* Return the next argument, or call the usage function.
* This handles both: -oFOO -o FOO
*/
static const char *get_opt_arg(void){
const char *ret;
ret = flagptr+1; /* assume argument is part of ps_argv[thisarg] */
if(*ret) return ret;
if(++thisarg >= ps_argc) usage(); /* there is nothing left */
/* argument is the new ps_argv[thisarg] */
ret = ps_argv[thisarg];
if(!ret || !*ret) usage();
return ret;
}
/* return the PID, or 0 if nothing good */
static void parse_pid(const char *str){
char *endp;
int num;
if(!str) goto bad;
num = strtol(str, &endp, 0);
if(*endp != '\0') goto bad;
if(num>0x7fff) goto bad; /* Linux PID limit */
if(num<1) goto bad;
if(want_one_pid) goto bad;
want_one_pid = num;
return;
bad:
usage();
}
/***************** parse SysV options, including Unix98 *****************/
static void parse_sysv_option(void){
do{
switch(*flagptr){
/**** selection ****/
case 'C': /* end */
if(want_one_command) usage();
want_one_command = get_opt_arg();
return; /* can't have any more options */
case 'p': /* end */
parse_pid(get_opt_arg());
return; /* can't have any more options */
case 'A':
case 'e':
select_all++;
select_notty++;
case 'w': /* here for now, since the real one is not used */
break;
/**** output format ****/
case 'f':
show_args = 1;
/* FALL THROUGH */
case 'j':
case 'l':
if(ps_format) usage();
ps_format = *flagptr;
break;
case 'o': /* end */
/* We only support a limited form: "ps -o pid=" (yes, just "pid=") */
if(strcmp(get_opt_arg(),"pid=")) usage();
if(ps_format) usage();
ps_format = 'o';
old_h_option++;
return; /* can't have any more options */
/**** other stuff ****/
#if 0
case 'w':
w_count++;
break;
#endif
default:
usage();
} /* switch */
}while(*++flagptr);
}
/************************* parse BSD options **********************/
static void parse_bsd_option(void){
do{
switch(*flagptr){
/**** selection ****/
case 'a':
select_all++;
break;
case 'x':
select_notty++;
break;
case 'p': /* end */
parse_pid(get_opt_arg());
return; /* can't have any more options */
/**** output format ****/
case 'j':
case 'l':
case 'u':
case 'v':
if(ps_format) usage();
ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */
break;
/**** other stuff ****/
case 'c':
bsd_c_option++;
#if 0
break;
#endif
case 'w':
#if 0
w_count++;
#endif
break;
case 'h':
old_h_option++;
break;
default:
usage();
} /* switch */
}while(*++flagptr);
}
#if 0
/* not used yet */
static void choose_dimensions(void){
struct winsize ws;
char *columns;
/* screen_cols is 80 by default */
if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col;
columns = getenv("COLUMNS");
if(columns && *columns){
long t;
char *endptr;
t = strtol(columns, &endptr, 0);
if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t;
}
if(w_count && (screen_cols<132)) screen_cols=132;
if(w_count>1) screen_cols=999999999;
}
#endif
static void arg_parse(int argc, char *argv[]){
int sel = 0; /* to verify option sanity */
ps_argc = argc;
ps_argv = argv;
thisarg = 0;
/**** iterate over the args ****/
while(++thisarg < ps_argc){
flagptr = ps_argv[thisarg];
switch(*flagptr){
case '0' ... '9':
show_args = 1;
parse_pid(flagptr);
break;
case '-':
flagptr++;
parse_sysv_option();
break;
default:
show_args = 1;
parse_bsd_option();
break;
}
}
/**** sanity check and clean-up ****/
if(want_one_pid) sel++;
if(want_one_command) sel++;
if(select_notty || select_all) sel++;
if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage();
if(bsd_c_option) show_args = 0;
}
/* return 1 if it works, or 0 for failure */
static int stat2proc(int pid) {
char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
int num;
int fd;
char* tmp;
struct stat sb; /* stat() used to get EUID */
snprintf(buf, 32, "/proc/%d/stat", pid);
if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
num = read(fd, buf, sizeof buf - 1);
fstat(fd, &sb);
P_euid = sb.st_uid;
close(fd);
if(num<80) return 0;
buf[num] = '\0';
tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<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, &P_tpgid,
&P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
&P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_it_real_value,
&P_start_time, &P_vsize,
&P_rss,
&P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
&P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
&P_wchan, &P_nswap, &P_cnswap
);
/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
P_vsize /= 1024;
P_rss *= (PAGE_SIZE/1024);
if(num < 30) return 0;
if(P_pid != pid) return 0;
return 1;
}
static const char *do_time(unsigned long t){
int hh,mm,ss;
static char buf[32];
int cnt = 0;
t /= HZ;
ss = t%60;
t /= 60;
mm = t%60;
t /= 60;
hh = t%24;
t /= 24;
if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss);
return buf;
}
static const char *do_user(void){
static char buf[32];
static struct passwd *p;
static int lastuid = -1;
if(P_euid != lastuid){
p = getpwuid(P_euid);
if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name);
else snprintf(buf, sizeof buf, "%5d ", P_euid);
}
return buf;
}
static const char *do_cpu(int longform){
static char buf[8];
strcpy(buf," - ");
if(!longform) buf[2] = '\0';
return buf;
}
static const char *do_mem(int longform){
static char buf[8];
strcpy(buf," - ");
if(!longform) buf[2] = '\0';
return buf;
}
static const char *do_stime(void){
static char buf[32];
strcpy(buf," - ");
return buf;
}
static void print_proc(void){
char tty[16];
snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty>>8)&0xff, P_tty&0xff);
switch(ps_format){
case 0:
printf("%5d %s %s", P_pid, tty, do_time(P_utime+P_stime));
break;
case 'o':
printf("%d\n", P_pid);
return; /* don't want the command */
case 'l':
printf(
"%03x %c %5d %5d %5d %s %3d %3d - "
"%5ld %06x %s %s",
(unsigned)P_flags&0x777, P_state, P_euid, P_pid, P_ppid, do_cpu(0),
(int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024),
(unsigned)(P_wchan&0xffffff), tty, do_time(P_utime+P_stime)
);
break;
case 'f':
printf(
"%8s %5d %5d %s %s %s %s",
do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), tty, do_time(P_utime+P_stime)
);
break;
case 'j':
printf(
"%5d %5d %5d %s %s",
P_pid, P_pgrp, P_session, tty, do_time(P_utime+P_stime)
);
break;
case 'u'|0x80:
printf(
"%8s %5d %s %s %5ld %4ld %s %c %s %s",
do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, tty, P_state,
do_stime(), do_time(P_utime+P_stime)
);
break;
case 'v'|0x80:
printf(
"%5d %s %c %s %6d - - %5d %s",
P_pid, tty, P_state, do_time(P_utime+P_stime), (int)P_maj_flt,
(int)P_rss, do_mem(1)
);
break;
case 'j'|0x80:
printf(
"%5d %5d %5d %5d %s %5d %c %5d %s",
P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime)
);
break;
case 'l'|0x80:
printf(
"%03x %5d %5d %5d %3d %3d "
"%5ld %4ld %06x %c %s %s",
(unsigned)P_flags&0x777, P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice,
P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, tty, do_time(P_utime+P_stime)
);
break;
default:
}
if(show_args) printf(" [%s]\n", P_cmd);
else printf(" %s\n", P_cmd);
}
int main(int argc, char *argv[]){
arg_parse(argc, argv);
#if 0
choose_dimensions();
#endif
if(!old_h_option){
const char *head;
switch(ps_format){
default: /* can't happen */
case 0: head = " PID TTY TIME CMD"; break;
case 'l': head = " F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break;
case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break;
case 'j': head = " PID PGID SID TTY TIME CMD"; break;
case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break;
case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break;
case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break;
case 'l'|0x80: head = " F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break;
}
printf("%s\n",head);
}
if(want_one_pid){
if(stat2proc(want_one_pid)) print_proc();
else exit(1);
}else{
struct direct *ent; /* dirent handle */
DIR *dir;
int ouruid;
int found_a_proc;
found_a_proc = 0;
ouruid = getuid();
dir = opendir("/proc");
while(( ent = readdir(dir) )){
if(*ent->d_name<'0' || *ent->d_name>'9') continue;
if(!stat2proc(atoi(ent->d_name))) continue;
if(want_one_command){
if(strcmp(want_one_command,P_cmd)) continue;
}else{
if(!select_notty && P_tty==-1) continue;
if(!select_all && P_euid!=ouruid) continue;
}
found_a_proc++;
print_proc();
}
closedir(dir);
exit(!found_a_proc);
}
return 0;
}

356
oldps.1 Normal file
View File

@ -0,0 +1,356 @@
.\" This file Copyright 1992, 1997 Michael K. Johnson <johnsonm@redhat.com>
.\" and 1996 Charles L. Blake <cblake@bbn.com>
.\" It may be distributed under the GNU Public License, version 2, or
.\" any higher version. See section COPYING of the GNU Public license
.\" for conditions under which this file may be redistributed.
.TH PS 1 "3 Sep 1997" "Cohesive Systems" "Linux User's Manual"
.SH NAME
ps \- report process status
.SH SYNOPSIS
ps [\fBlujsvmaxScewhrnu\fR] [\fBt\fIxx\fR] \
[\fBO\fR[\fB+\fR|\fB-\fR]\fIk1\fR[[\fB+\fR|\fB-\fR]\fIk2\fR...]] \
[\fIpids\fR]
there are also three long options:
.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]]
.B "\-\-help"
.B "\-\-version"
More long options are on the way...
.SH DESCRIPTION
.B "ps "
gives a snapshot of the current processes. If you want a repetitive
update of this status, use
.BR top .
This man page documents the
.IR /proc -based
version of
.BR ps ,
or tries to.
.PP
.SH "COMMAND-LINE OPTIONS"
The command-line options for this version of
.B ps
are derived from the BSD version of
.BR ps ,
not the System V version.
The command-line arguments should \fBnot\fP be preceeded by a `\-' character,
because in the future, a `\-' will be used to indicate Unix98-standard
command-line arguments, while no `\-' will indicate the current
``extended BSD'' style of command line arguments.
For now, ps will give you a warning if you use a `\-' for a short option,
but it will still work. If you have shell scripts which use BSD-style
arguments to ps, take heed of the warning and fix them, or else your
scripts will fail to function correctly at some point in the future.
If you want to turn off the warnings, set the
.B I_WANT_A_BROKEN_PS
environment variable.
There are also some ``long options'' in GNU style; see below for those.
.PP
.PD 0
.TP 0.5i
.B "l "
long format
.TP 0.5i
.B "u "
user format: gives user name and start time
.TP 0.5i
.B "j "
jobs format: pgid sid
.TP 0.5i
.B "s"
signal format
.TP 0.5i
.B "v "
vm format
.TP 0.5i
.B "m "
displays memory info (combine with
.B p
flag to get number of pages).
.TP 0.5i
.B "f "
"forest" family tree format for command line
.TP 0.5i
.B "a "
show processes of other users too
.TP 0.5i
.B "x "
show processes without controlling terminal
.TP 0.5i
.B "S "
add child cpu time and page faults
.TP 0.5i
.B "c "
command name from task_struct
.TP 0.5i
.B "e "
show environment after command line and ` + '
.TP 0.5i
.B "w "
wide output: don't truncate command lines to fit on one line.
To be exact, every w that is specified will add another possible
line to the output. If the space isn't needed it isn't used. You
may up to 100
.BR w 's.
.TP 0.5i
.B "h "
no header
.TP 0.5i
.B "r "
running procs only
.TP 0.5i
.B "n "
numeric output for
.BR USER " and " WCHAN .
.PD 1
.TP 0.5i
.BI t xx
only procs with controlling tty \fIxx\fR; for \fIxx\fR you may use either the
name of a device file under "/dev" or that name with either
.IR tty " or " cu
sliced off. This is the reverse heuristic that ps uses to print out the
abbreviated tty name in the \fBTT\fR field, e.g.
.BR "ps t1" .
.TP 0.5i
.BR O [ + | - ] \fIk1 [,[ + | - ] \fIk2 [, ... ]]
Order the process listing according to the multi-level sort specified by
the sequence of \fIshort\fR keys from \fBSORT KEYS\fR, \fIk1\fR, \fIk2\fR, ...
Default order specifications exist for each of the various formats of \fBps\fR.
These are over-ridden by a user specified ordering. The `+' is quite optional,
merely re-iterating the default direction on a key. `-' reverses direction only
on the key it precedes. As with \fBt\fR and \fIpids\fR, the O option must be
the last option in a single command argument, but specifications in successive
arguments are catenated.
.TP 0.5i
.I pids
List only the specified processes; they are comma-delimited. The
list must be given immediately after the last option in a single command-line
argument, with no intervening space, e.g.
.BR "ps j1,4,5" .
Lists specified in subsequent arguments are catenated, e.g.
.B ps l 1,2 3,4 5 6
will list all of the processes 1-6 in long format. If pids are given, they
are listed no matter what. If a tty is given matching processes are listed
no matter what. These two features override the 'a' and 'x' flags.
.SH "LONG COMMAND\-LINE OPTIONS"
These options are preceeded by a double\-hyphen.
.TP 0.5i
.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]]
Choose a \fImulti-letter key\fR from the \fBSORT KEYS\fR section. \fIX\fR may be
any convenient separator character. To be GNU-ish use `='. The `+' is really
optional since default direction is increasing numerical or lexicographic order.
E.g.:
.B ps jax --sort=uid,-ppid,+pid
.TP 0.5i
.B "\-\-help"
Get a help message that summarizes the usage and gives a list of
supported sort keys. This list may be more up to date than this man
page.
.TP 0.5i
.B "\-\-version"
Display version and source of this program.
.SH "SORT KEYS"
Note that the values used in sorting are the internal values \fBps\fR uses and
\fInot\fR the `cooked' values used in some of the output format fields. If
someone wants to volunteer to write special comparison functions for the cooked
values, ... ;-)
SHORT LONG DESCRIPTION
.PD 0
.TP 0.5i
c cmd simple name of executable
.TP 0.5i
C cmdline full command line
.TP 0.5i
f flags flags as in long format F field
.TP 0.5i
g pgrp process group ID
.TP 0.5i
G tpgid controlling tty process group ID
.TP 0.5i
j cutime cumulative user time
.TP 0.5i
J cstime cumulative system time
.TP 0.5i
k utime user time
.TP 0.5i
K stime system time
.TP 0.5i
m min_flt number of minor page faults
.TP 0.5i
M maj_flt number of major page faults
.TP 0.5i
n cmin_flt cumulative minor page faults
.TP 0.5i
N cmaj_flt cumulative major page faults
.TP 0.5i
o session session ID
.TP 0.5i
p pid process ID
.TP 0.5i
P ppid parent process ID
.TP 0.5i
r rss resident set size
.TP 0.5i
R resident resident pages
.TP 0.5i
s size memory size in kilobytes
.TP 0.5i
S share amount of shared pages
.TP 0.5i
t tty the minor device number of tty
.TP 0.5i
T start_time time process was started
.TP 0.5i
U uid user ID number
.TP 0.5i
u user user name
.TP 0.5i
v vsize total VM size in bytes
.TP 0.5i
y priority kernel scheduling priority
.PD 1
.SH "FIELD DESCRIPTIONS"
.TP 0.5i
.B "PRI "
This is the counter field in the task struct. It is the time in
.B HZ
of the process's possible timeslice.
.TP 0.5i
.B "NI "
Standard unix nice value; a positive value means less cpu time.
.TP 0.5i
.B "SIZE "
Virtual image size; size of text+data+stack.
.TP 0.5i
.B "RSS "
Resident set size; kilobytes of program in memory.
.TP 0.5i
.B "WCHAN "
Name of the kernel function where the process is sleeping, with the
.RB ` sys_ '
stripped from the function name. If
.I /etc/psdatabase
does not exist, it is just a hex number instead.
.TP 0.5i
.B "STAT "
Information about the status of the process. The first field is
.B R
for runnable,
.B S
for sleeping,
.B D
for uninterruptible sleep,
.B T
for stopped or traced, or
.B Z
for a zombie process. The second field contains
.B W
if the process has no resident pages. The third field is
.B N
if the process has a positive nice value
.RB ( NI
field).
.TP 0.5i
.B "TT "
Controlling tty.
.TP 0.5i
.B "PAGEIN "
Number of major page faults (page faults that cause pages to be read
from disk, including pages read from the buffer cache).
.TP 0.5i
.B "TRS "
Text resident size.
.TP 0.5i
.B "SWAP "
Kilobytes (or pages if
.B p
is used) on swap device.
.TP 0.5i
.B "SHARE "
Shared memory.
.SH UPDATING
This
.BR proc -based
.B ps
works by reading the files in the
.B proc
filesystem, mounted on
.BR /proc .
This
.B ps
does not need to be suid
.B kmem
or have any privileges to run.
.I "Do not give this ps any special permissions."
.PP
You will need to put in place the appropriate System.map file
when you install a new kernel in order
to get meaningful information from the
.B WCHAN
field. This should be done every time you compile a new kernel. You should
also run 'ps' as root once and then any time the tty devices in the "/dev"
directory change.
As of procps-1.00, ps/top read System.map directly if it is available. The
search path for kernel address-to-symbol resolution is:
.nf
$PS_SYSTEM_MAP
/boot/System.map-`uname -r`
/boot/System.map
/lib/modules/`uname -r`/System.map
/etc/psdatabase
/boot/psdatabase-`uname -r`
/boot/psdatabase,
/lib/modules/`uname -r`/psdatabase
.fi
.PP
.SH NOTES
The member
.B used_math
of
.B task_struct
is not shown, since
.B crt0.s
checks to see if math is present. This causes the math flag to be set
for all processes, and so it is worthless.
.PP
Programs swapped out to disk will be shown without command line
arguments, and unless the
.B c
option is given, in parentheses.
.PP
.B %CPU
shows the cputime/realtime percentage. It will not add up to 100%
unless you are lucky. It is time used divided by the time the process
has been running.
.PP
The
.B SIZE
and
.B RSS
fields don't count the page tables and the
.B task_struct
of a proc; this is at least 12k of memory that is always resident.
.B SIZE
is the virtual size of the proc (code+data+stack).
.SH AUTHOR
.B ps
was originally written by Branko Lankester <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. Michael K. Johnson
<johnsonm@redhat.com> is the current maintainer.
Please send bug reports to <acahalan@cs.uml.edu>

660
oldps.c Normal file
View File

@ -0,0 +1,660 @@
/*
* ps.c - show process status
*
* Copyright (c) 1992 Branko Lankester
*
* Snarfed and HEAVILY modified for procps by Michael K. Johnson,
* (johnsonm@sunsite.unc.edu). What is used is what is required to have a
* common interface.
*
* Massive modifications by Charles Blake (cblake@bbn.com). Rewrite
* of the system process table code, multilevel sorting, device number
* database, forest feature (contributed by ...), environment variables, GNU
* style long options, pid list filtering (contributed by Michael Shields).
*
* Michael K. Johnson <johnsonm@redhat.com> again in charge, merging
* patches that have accumulated over a long time.
*
* Changes Copyright (C) 1993, 1994, 1997 Michael K. Johnson,
* and Copyright (C) 1995, 1996 Charles Blake
* See file COPYING for copyright details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <time.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <proc/version.h>
#include <proc/readproc.h>
#include <proc/procps.h>
#include <proc/devname.h>
#include <proc/tree.h>
#include <proc/sysinfo.h>
#include <proc/status.h>
#include <proc/compare.h>
static void show_short(char*, proc_t*);
static void show_long (char*, proc_t*);
static void show_user (char*, proc_t*);
static void show_jobs (char*, proc_t*);
static void show_sig (char*, proc_t*);
static void show_vm (char*, proc_t*);
static void show_m (char*, proc_t*);
static void show_regs (char*, proc_t*);
/* this struct replaces the previously parallel fmt_fnc and hdrs */
static struct {
void (*format)(char*,proc_t*);
char CL_option_char;
const char* default_sort;
const char* header;
} mode[] = {
{ show_short, 0 , "Up", " PID TTY STAT TIME COMMAND" },
{ show_long, 'l', "Pp", " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND" },
{ show_user, 'u', "up", "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND" },
{ show_jobs, 'j', "gPp"," PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND" },
{ show_sig, 's', "p", " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND" },
{ show_vm, 'v', "r", " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND" },
{ show_m, 'm', "r", " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND" },
{ show_regs, 'X', "p", "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND" },
{ NULL, 0, NULL, NULL }
};
/* some convenient integer constants corresponding to the above rows. */
enum PS_MODALITY { PS_D = 0, PS_L, PS_U, PS_J, PS_S, PS_V, PS_M, PS_X };
static char * prtime (char *s, unsigned long t, unsigned long rel);
static void usage (char* context);
static void set_cmdspc (int w_opts);
static void show_procs (unsigned maxcmd, int do_header, int, void*,int);
static void show_cmd_env (char* tskcmd, char** cmd, char** env, unsigned maxch);
static void show_a_proc (proc_t* the_proc, unsigned maxcmd);
static void show_time (char *s, proc_t * p);
static void add_node (char *s, proc_t *task);
static int node_cmp (const void *s1, const void *s2);
static void show_tree (int n, int depth, char *continued);
static void show_forest (void);
static int CL_fmt = 0;
/* process list filtering command line options */
static int CL_all,
CL_kern_comm,
CL_no_ctty,
CL_run_only;
static char * CL_ctty;
static pid_t * CL_pids; /* zero-terminated list, dynamically allocated */
/* process display modification command line options */
static int CL_show_env,
CL_num_outp, /* numeric fields for user, wchan, tty */
CL_sort = 1,
CL_forest,
CL_Sum,
CL_pg_shift = (PAGE_SHIFT - 10); /* default: show k instead of pages */
/* Globals */
static unsigned cmdspc = 80; /* space left for cmd+env after table per row */
static int GL_current_time; /* some global system parameters */
static long GL_time_now;
static int GL_wchan_nout; /* this is can also be set on the command-line */
/*****************************/
int main(int argc, char **argv) {
char *p;
int width = 0,
do_header = 1,
user_ord = 0,
next_arg = 0,
toppid = 0,
pflags, N = 1;
void* args = NULL;
dev_t tty[2] = { 0 };
uid_t uid[1];
do {
--argc; /* shift to next arg. */
++argv;
for (p = *argv; p && *p; ++p) {
switch (*p) {
case '-': /* "--" ==> long name options */
if (*(p+1) == '-') {
if (strncmp(p+2,"sort",4)==0) {
if (parse_long_sort(p+7))
usage("unrecognized long sort option\n");
user_ord = 1;
next_arg = 1;
break;
} else if (strncmp(p+2, "help", 4) == 0) {
usage(NULL);
return 0;
} else if (strncmp(p+2, "version", 6) == 0) {
display_version();
return 0;
} else if (*(p+2) != '\0') /* just '-- '; not an error */
usage("ps: unknown long option\n");
}
if (!getenv("I_WANT_A_BROKEN_PS")) {
fprintf(stderr,
"warning: `-' deprecated; use `ps %s', not `ps %s'\n",
(p+1), p);
}
break;
case 'l': CL_fmt = PS_L; /* replaceable by a */ break;
case 'u': CL_fmt = PS_U; /* loop over mode[] */ break;
case 'j': CL_fmt = PS_J; break;
case 's': CL_fmt = PS_S; break;
case 'v': CL_fmt = PS_V; break;
case 'm': CL_fmt = PS_M; break;
case 'X': CL_fmt = PS_X; /* regs */ break;
case 'r': CL_run_only = 1; /* list filters */ break;
case 'a': CL_all = 1; break;
case 'x': CL_no_ctty = 1; break;
case 't': CL_ctty = p + 1;
next_arg = 1; break;
case 'e': CL_show_env = 1; /* output modifiers */ break;
case 'f': CL_forest = 1;
CL_kern_comm = 0; break;
case 'c': CL_kern_comm = 1; break;
case 'w': ++width; break;
case 'h': do_header = 0; break;
case 'n': CL_num_outp = 1;
GL_wchan_nout = 1; break;
case 'S': CL_Sum = 1; break;
case 'p': CL_pg_shift = 0; break;
case 'o': CL_sort = !CL_sort; break;
case 'O':
if (parse_sort_opt(p+1))
usage("short form sort flag parse error\n");
user_ord = 1;
next_arg = 1;
break;
case 'V': display_version(); exit(0);
default:
/* Step through, reading+alloc space for comma-delim pids */
if (isdigit(*p)) {
while (isdigit(*p)) {
CL_pids = xrealloc(CL_pids, (toppid + 2)*sizeof(pid_t));
CL_pids[toppid++] = atoi(p);
while (isdigit(*p))
p++;
if (*p == ',')
p++;
}
CL_pids[toppid] = 0;
next_arg = 1;
}
if (*p)
usage("unrecognized option or trailing garbage\n");
}
if (next_arg) {
next_arg = 0;
break; /* end loop over chars in this argument */
}
}
} while (argc > 1);
if (!CL_sort) /* since the unsorted mode is intended to be speedy */
CL_forest = 0; /* turn off the expensive forest option as well. */
if (CL_fmt == PS_L) {
if (open_psdb(NULL)) GL_wchan_nout = 1;
}
set_cmdspc(width);
if (!(GL_current_time = uptime(0,0)))
return 1;
GL_time_now = time(0L);
if (CL_sort && !user_ord)
parse_sort_opt(mode[CL_fmt].default_sort);
/* NOTE: all but option parsing has really been done to enable
* multiple uid/tty/state filtering as well as multiple pid filtering
*/
pflags = PROC_ANYTTY; /* defaults */
if (!CL_kern_comm) pflags |= PROC_FILLCMD; /* verbosity flags */
if (CL_fmt == PS_M) pflags |= PROC_FILLMEM;
if (CL_show_env) pflags |= PROC_FILLENV;
if (!CL_num_outp) pflags |= PROC_FILLUSR;
if (CL_no_ctty) pflags &= ~PROC_ANYTTY; /* filter flags */
if (CL_run_only) { pflags |= PROC_STAT; args = "RD"; }
else if (!CL_all) { pflags |= PROC_UID; args = uid; uid[0] = getuid(); pflags &= ~PROC_STAT; }
if (CL_pids) { pflags |= PROC_PID; args = CL_pids; pflags &= ~PROC_UID; pflags &= ~PROC_STAT; }
if (CL_ctty) {
if ((tty[0] = tty_to_dev(CL_ctty)) == (dev_t)-1) {
fprintf(stderr, "the name `%s' is not a tty\n", CL_ctty);
exit(1);
}
pflags = (pflags | PROC_TTY) & ~(PROC_ANYTTY|PROC_STAT|PROC_UID|PROC_PID);
args = tty;
}
show_procs(cmdspc, do_header, pflags, args, N);
return 0;
}
/***************************/
/* print a context dependent usage message and maybe exit */
static void usage(char* context) {
fprintf(stderr,
"%s"
"usage: ps acehjlnrsSuvwx{t<tty>|#|O[-]u[-]U..} \\\n"
" --sort:[-]key1,[-]key2,...\n"
" --help gives you this message\n"
" --version prints version information\n",
context ? context : "");
if (context)
exit(1); /* prevent bad exit status by calling usage(NULL) */
}
/*****************************/
/* set maximum chars displayed on a line based on screen size.
* Always allow for the header, with n+1 lines of output per row.
*/
static void set_cmdspc(int n) {
struct winsize win;
int h = strlen(mode[CL_fmt].header),
c = strlen("COMMAND");
if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
cmdspc = win.ws_col;
if (n > 100) n = 100; /* max of 100 'w' options */
if (cmdspc > h)
cmdspc = cmdspc*(n+1) - h + c;
else
cmdspc = cmdspc*n + c;
}
/*****************************/
static const char *ttyc(int tty, int pid){
static char outbuf[8];
if(CL_num_outp)
snprintf(outbuf, sizeof outbuf, "%4.4x", tty);
else
dev_to_tty(outbuf, 4, tty, pid, ABBREV_TTY|ABBREV_DEV|ABBREV_PTS);
return outbuf;
}
/*****************************/
/* This is the main driver routine that iterates over the process table.
*/
static void show_procs(unsigned maxcmd, int do_header, int pflags, void* args, int N) {
static proc_t buf; /* less dynamic memory allocation when not sorting */
PROCTAB* tab;
proc_t **ptable = NULL, *next, *retbuf = NULL;
int n = 0;
/* initiate process table scan */
tab = openproc(pflags, args, N);
if (!tab) {
fprintf(stderr, "Error: can not access /proc.\n");
exit(1);
}
if (do_header) puts(mode[CL_fmt].header); /* print header */
if (!(CL_sort || CL_forest)) /* when sorting and forest are both */
retbuf = &buf; /* off we can use a static buffer */
while ((next = readproc(tab,retbuf))) { /* read next process */
n++; /* Now either: */
if (CL_forest) { /* add process to tree */
/* FIXME: this static is disgusting, but to keep binary
* compatibility within the 1.2 series I'll just go from
* 256 bytes to 1024 bytes, which should be good enough
* for most people.... :-( In the future, the format
* format function should probably dynamically allocate
* strings.
*/
static char s[1024];
(mode[CL_fmt].format)(s, next);
if (CL_fmt != PS_V && CL_fmt != PS_M)
show_time(s+strlen(s), next);
add_node(s, next);
} else if (CL_sort) { /* add process to table */
ptable = xrealloc(ptable, n*sizeof(proc_t*));
ptable[n-1] = next;
} else { /* or show it right away */
show_a_proc(&buf, maxcmd);
if (buf.cmdline) free((void*)(buf.cmdline[0]));
if (buf.environ) free((void*)(buf.environ[0]));
}
}
if (!n) {
fprintf(stderr, "No processes available.\n");
exit(1);
}
if (CL_sort && !CL_forest) { /* just print sorted table */
int i;
qsort(ptable, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
for (i = 0; i < n; i++) {
show_a_proc(ptable[i], maxcmd);
freeproc(ptable[i]);
}
free(ptable);
} else if (CL_forest)
show_forest();
}
/*****************************/
/* show the trailing command and environment in available space.
* use abbreviated cmd if requested, NULL list, or singleton NULL string
*/
static void show_cmd_env(char* tskcmd, char** cmd, char** env, unsigned maxch) {
if (CL_kern_comm) /* no () when explicit request for tsk cmd */
maxch = print_str(stdout, tskcmd, maxch);
else if (!cmd || !*cmd || (!cmd[1] && !*cmd)) {
/* no /proc//cmdline ==> bounding () */
if (maxch) {
fputc('(', stdout);
maxch--;
}
maxch = print_str(stdout, tskcmd, maxch);
if (maxch) {
fputc(')', stdout);
maxch--;
}
} else
maxch = print_strlist(stdout, cmd, " ", maxch);
if (CL_show_env && env)
print_strlist(stdout, env, " ", maxch);
fputc('\n', stdout);
}
/*****************************/
/* format a single process for output.
*/
static void show_a_proc(proc_t* p, unsigned maxch) {
static char s[2048];
(mode[CL_fmt].format)(s, p);
if (CL_fmt != PS_V && CL_fmt != PS_M)
show_time(s+strlen(s), p);
printf("%s", s);
show_cmd_env(p->cmd, p->cmdline, p->environ, maxch);
}
/****** The format functions for the various formatting modes follow *****/
/*****************************/
static void show_short(char *s, proc_t *p) {
sprintf(s, "%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p));
}
/*****************************/
static void show_long(char *s, proc_t *p) {
char wchanb[32];
if (GL_wchan_nout)
sprintf(wchanb, " %-9lx ", p->wchan & 0xffffffff);
else
sprintf(wchanb, "%-11.11s", wchan(p->wchan));
sprintf(s, "%6lx %5d %5d %5d %3ld %3ld %6ld %5ld %-11.11s %s%3s",
p->flags, p->euid, p->pid, p->ppid, p->priority, p->nice,
p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), wchanb, status(p),
ttyc(p->tty,p->pid));
}
/*****************************/
static void show_jobs(char *s, proc_t *p) {
sprintf(s, "%5d %5d %5d %5d %3s %5d %s %5d ",
p->ppid, p->pid, p->pgrp, p->session, ttyc(p->tty,p->pid), p->tpgid, status(p),
p->euid);
}
/*****************************/
static void show_user(char *s, proc_t *p) {
long pmem, total_time, seconds;
time_t start;
unsigned int pcpu;
if (CL_num_outp)
s += sprintf(s, "%5d ", p->euid);
else
s += sprintf(s, "%-8s ", p->euser);
/* Hertz, being UL, forces 64-bit calculation on 64-bit machines */
seconds = (((GL_current_time * Hertz) - p->start_time) / Hertz);
start = GL_time_now - seconds;
total_time = (p->utime + p->stime +
(CL_Sum ? p->cutime + p->cstime : 0));
pcpu = seconds ?
(total_time * 10 * 100 / Hertz) / seconds :
0;
if (pcpu > 999) pcpu = 999;
pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10));
sprintf(s, "%5d %2u.%u %2ld.%ld %5ld %5ld %3s %s %.6s ",
p->pid, pcpu / 10, pcpu % 10, pmem / 10, pmem % 10,
p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), ttyc(p->tty,p->pid), status(p),
ctime(&start) + (GL_time_now - start > 3600*24 ? 4 : 10));
}
/*****************************/
/* TODO: this is broken... but does anyone care? */
static void show_sig(char *s, proc_t *p) {
#undef SIGNAL_STRING /* not filled in by the function oldps calls */
#ifdef SIGNAL_STRING
#define TAIL(s) ((s)+(long)strlen(s)-8)
sprintf(s, "%5d %5d %s %s %s %s %s %3s ",
p->euid, p->pid,
TAIL(p->signal), TAIL(p->blocked), TAIL(p->sigignore), TAIL(p->sigcatch),
status(p), ttyc(p->tty,p->pid)
);
#else
sprintf(s, "%5d %5d %08x %08x %08x %08x %s %3s ",
p->euid, p->pid,
(unsigned)p->signal, (unsigned)p->blocked, (unsigned)p->sigignore, (unsigned)p->sigcatch,
status(p), ttyc(p->tty,p->pid)
);
#endif
}
/*****************************/
static void show_vm(char *s, proc_t *p) {
int pmem;
s += sprintf(s,"%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p));
show_time(s, p);
s += strlen(s);
s += sprintf(s, " %6ld %4ld %4ld %4ld ",
p->maj_flt + (CL_Sum ? p->cmaj_flt : 0),
p->vsize ? (p->end_code - p->start_code) >> 10 : 0,
p->vsize ? (p->vsize - p->end_code + p->start_code) >> 10 : 0,
p->rss << (PAGE_SHIFT - 10));
if(p->rss_rlim == RLIM_INFINITY)
s += sprintf(s, " xx ");
else
s += sprintf(s, "%5ld ", p->rss_rlim >> 10);
pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10));
sprintf(s, "%2d.%d ", pmem / 10, pmem % 10);
}
/*****************************/
static void show_m(char *s, proc_t *p) {
sprintf(s, "%5d %3s %6ld %6ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %3ld ",
p->pid, ttyc(p->tty,p->pid),
p->maj_flt + (CL_Sum ? p->cmaj_flt : 0),
p->min_flt + (CL_Sum ? p->cmin_flt : 0),
p->trs << CL_pg_shift,
p->drs << CL_pg_shift,
p->size << CL_pg_shift,
(p->size - p->resident) << CL_pg_shift,
p->resident << CL_pg_shift,
p->share << CL_pg_shift,
p->lrs << CL_pg_shift,
p->dt);
}
/*****************************/
static void show_regs(char *s, proc_t *p) {
char time1[16], time2[16];
sprintf(s, "%2ld %5d %8lx %8lx %8lx %s %s %s %3s ",
p->start_code >> 26, p->pid, p->start_stack,
p->kstk_esp, p->kstk_eip,
prtime(time1, p->timeout, GL_current_time*Hertz),
prtime(time2, p->it_real_value, 0),
status(p), ttyc(p->tty,p->pid));
}
/*****************************/
static char *prtime(char *s, unsigned long t, unsigned long rel) {
if (t == 0) {
sprintf(s, " ");
return s;
}
if ((long) t == -1) {
sprintf(s, " xx");
return s;
}
if ((long) (t -= rel) < 0)
t = 0;
if (t > 9999)
sprintf(s, "%5lu", t / 100);
else
sprintf(s, "%2lu.%02lu", t / 100, t % 100);
return s;
}
/*****************************/
static void show_time(char *s, proc_t * p) {
unsigned t;
t = (p->utime + p->stime) / Hertz;
if (CL_Sum) t += (p->cutime + p->cstime) / Hertz;
sprintf(s, "%3d:%02d ", t / 60, t % 60);
}
/*****************************/
/* fancy process family tree based cmdline printing. Building the tree
should be relegated to libproc and only the printing logic should
remain here.
*/
static struct tree_node * node; /* forest mode globals */
static int nodes = 0;
static int maxnodes = 0;
/*****************************/
static void add_node(char *s, proc_t *task) {
if (maxnodes == 0) {
maxnodes = 64;
node = (struct tree_node *)
xmalloc(sizeof(struct tree_node) * maxnodes);
}
if (nodes >= maxnodes) {
maxnodes *= 2;
node = (struct tree_node *)
xrealloc(node, sizeof(struct tree_node) * maxnodes);
}
node[nodes].proc = task;
node[nodes].pid = task->pid;
node[nodes].ppid = task->ppid;
node[nodes].line = strdup(s);
node[nodes].cmd = task->cmd;
node[nodes].cmdline = task->cmdline;
node[nodes].environ = task->environ;
node[nodes].children = 0;
node[nodes].have_parent = 0;
nodes++;
}
/*****************************/
static int node_cmp(const void *s1, const void *s2) {
struct tree_node *n1 = (struct tree_node *) s1;
struct tree_node *n2 = (struct tree_node *) s2;
return n1->pid - n2->pid;
}
/*****************************/
static void show_tree(int n, int depth, char *continued) {
int i, cols = 0;
fprintf(stdout, "%s", node[n].line);
for (i = 0; i < depth; i++) {
if (cols + 4 >= cmdspc - 1)
break;
if (i == depth - 1)
printf(" \\_ ");
else if (continued[i])
printf(" | ");
else
printf(" ");
cols += 4;
}
show_cmd_env(node[n].cmd, node[n].cmdline, node[n].environ, cmdspc - cols);
for (i = 0; i < node[n].children; i++) {
continued[depth] = i != node[n].children - 1;
show_tree(node[n].child[i], depth + 1, continued);
}
}
/*****************************/
static void show_forest() {
register int i, j;
int parent;
char continued[1024];
if (CL_sort)
qsort((void*)node, nodes, sizeof(struct tree_node), (void*)node_mult_lvl_cmp);
for (i = 0; i < nodes; i++) {
if (node[i].ppid > 1 && node[i].pid != node[i].ppid) {
parent = -1;
for (j=0; j<nodes; j++)
if (node[j].pid==node[i].ppid)
parent = j;
} else
parent = -1;
if (parent >= 0) {
node[i].have_parent++;
if (node[parent].children == 0) {
node[parent].child = (int*)xmalloc(16 * sizeof(int*));
node[parent].maxchildren = 16;
}
else if (node[parent].children == node[parent].maxchildren) {
node[parent].maxchildren *= 2;
node[parent].child = (int*)xrealloc(node[parent].child,
node[parent].maxchildren
* sizeof(int*));
}
node[parent].child[node[parent].children++] = i;
}
}
for (i = 0; i < nodes; i++) {
if (!node[i].have_parent)
show_tree(i, 0, continued);
}
}

158
pgrep.1 Normal file
View File

@ -0,0 +1,158 @@
.\" Manual page for pgrep / pkill.
.\" Licensed under version 2 of the GNU General Public License.
.\" Copyright 2000 Kjetil Torgrim Homme
.\"
.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual"
.SH NAME
pgrep, pkill \- look up or signal processes based on name and other attributes
.SH SYNOPSIS
pgrep [\-flnvx] [\-d \fIdelimiter\fP] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
.br
[\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
.br
[\-t \fIterm\fP,...] [\fIpattern\fP]
pkill [\-\fIsignal\fP] [\-fnvx] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
.br
[\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
.br
[\-t \fIterm\fP,...] [\fIpattern\fP]
.SH DESCRIPTION
\fBpgrep\fP looks through the currently running processes and lists the
process IDs which matches the selection criteria to stdout. All
the criteria have to match. For example,
pgrep -u root sshd
will only list the processes called \fBsshd\fP AND owned by \fBroot\fP.
On the other hand,
pgrep -u root,daemon
will list the processes owned by \fBroot\fP OR \fBdaemon\fP.
\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP)
to each process instead of listing them on stdout.
.SH OPTIONS
.TP
\-d \fIdelimiter\fP
Sets the string used to delimit each process ID in the output (by
default a newline). (\fBpgrep\fP only.)
.TP
\-f
The \fIpattern\fP is normally only matched against the process name.
When \-f is set, the full command line is used.
.TP
\-g \fIpgrp\fP,...
Only match processes in the process group IDs listed. Process group 0
is translated into \fBpgrep\fP's or \fBpkill\fP's own process group.
.TP
\-G \fIgid\fP,...
Only match processes whose real group ID is listed. Either the
numerical or symbolical value may be used.
.TP
\-l
List the process name as well as the process ID. (\fBpgrep\fP only.)
.TP
\-n
Select only the newest (most recently started) of the matching
processes.
.TP
\-P \fIppid\fP,...
Only match processes whose parent process ID is listed.
.TP
\-s \fIsid\fP,...
Only match processes whose process session ID is listed. Session ID 0
is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID.
.TP
\-t \fIterm\fP,...
Only match processes whose controlling terminal is listed. The
terminal name should be specified without the "/dev/" prefix.
.TP
\-u \fIeuid\fP,...
Only match processes whose effective user ID is listed. Either the
numerical or symbolical value may be used.
.TP
\-U \fIuid\fP,...
Only match processes whose real user ID is listed. Either the
numerical or symbolical value may be used.
.TP
\-v
Negates the matching.
.TP
\-x
Only match processes whose name (or command line if \-f is specified)
\fBexactly\fP match the \fIpattern\fP.
.TP
\-\fIsignal\fP
Defines the signal to send to each matched process. Either the
numeric or the symbolic signal name can be used. (\fBpkill\fP only.)
.SH OPERANDS
.TP
\fIpattern\fP
Specifies an Extended Regular Expression for matching against the
process names or command lines.
.SH EXAMPLES
Example 1: Find the process ID of the \fBnamed\fP daemon:
unix$ pgrep \-u root named
Example 2: Make \fBsyslog\fP reread its configuration file:
unix$ pkill \-HUP syslogd
Example 3: Give detailed information on all \fBxterm\fP processes:
unix$ ps \-fp $(pgrep \-d, \-x xterm)
Example 4: Make all \fBnetscape\fP processes run nicer:
unix$ renice +4 `pgrep netscape`
.SH "EXIT STATUS"
.TP
.I "0"
One or more processes matched the criteria.
.TP
.I "1"
No processes matched.
.TP
.I "2"
Syntax error in the command line.
.TP
.I "3"
Fatal error: out of memory etc.
.SH NOTES
The process name used for matching is limited to the 15 characters
present in the output of /proc/\fIpid\fP/stat. Use the \-f option to
match against the complete command line, /proc/\fIpid\fP/cmdline.
The running \fBpgrep\fP or \fBpkill\fP process will never report
itself as a match.
.SH BUGS
The options \-n and \-v can not be combined. Let me know if you need
to do this.
Defunct processes are reported.
.SH "SEE ALSO"
ps(1) proc(5) regex(5)
.SH STANDARDS
\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This
implementation is fully compatible.
.SH AUTHOR
Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
Michael K. Johnson <johnsonm@redhat.com> is the current maintainer of
the procps package.
Please send bug reports to <procps-bugs@redhat.com>

605
pgrep.c Normal file
View File

@ -0,0 +1,605 @@
/* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*-
*
* pgrep/pkill -- utilities to filter the process table
*
* Copyright 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
*
* May be distributed under the conditions of the
* GNU General Public License; a copy is in COPYING
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.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"
static int i_am_pkill = 0;
char *progname = "pgrep";
union el {
long num;
char * str;
};
/* User supplied arguments */
static int opt_full = 0;
static int opt_long = 0;
static int opt_newest = 0;
static int opt_negate = 0;
static int opt_exact = 0;
static int opt_signal = SIGTERM;
static char *opt_delim = "\n";
static union el *opt_pgrp = NULL;
static union el *opt_gid = NULL;
static union el *opt_ppid = NULL;
static union el *opt_sid = NULL;
static union el *opt_term = NULL;
static union el *opt_euid = NULL;
static union el *opt_uid = NULL;
static char *opt_pattern = NULL;
/* Prototypes */
static union el *split_list (const char *, char, int (*)(const char *, union el *));
static int conv_uid (const char *, union el *);
static int conv_gid (const char *, union el *);
static int conv_sid (const char *, union el *);
static int conv_pgrp (const char *, union el *);
static int conv_num (const char *, union el *);
static int conv_str (const char *, union el *);
static int match_numlist (long, const union el *);
static int match_strlist (const char *, const union el *);
static int
usage (int opt)
{
if (i_am_pkill)
fprintf (stderr, "Usage: pgrep [-flnvx] [-d DELIM] ");
else
fprintf (stderr, "Usage: pkill [-SIGNAL] [-fnvx] ");
fprintf (stderr, "[-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n"
"\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] "
"[PATTERN]\n");
exit (opt == '?' ? 0 : 2);
}
static void
parse_opts (int argc, char **argv)
{
char opts[32] = "";
int opt;
int criteria_count = 0;
if (strstr (argv[0], "pkill")) {
i_am_pkill = 1;
progname = "pkill";
/* Look for a signal name or number as first argument */
if (argc > 1 && argv[1][0] == '-') {
int sig;
sig = signal_name_to_number (argv[1] + 1);
if (sig == -1 && isdigit (argv[1][1]))
sig = atoi (argv[1] + 1);
if (sig != -1) {
int i;
for (i = 2; i < argc; i++)
argv[i-1] = argv[i];
--argc;
opt_signal = sig;
}
}
} else {
/* These options are for pgrep only */
strcat (opts, "ld:");
}
strcat (opts, "fnvxP:g:s:u:U:G:t:?");
while ((opt = getopt (argc, argv, opts)) != -1) {
switch (opt) {
case 'f':
opt_full = 1;
break;
case 'l':
opt_long = 1;
break;
case 'n':
opt_newest = 1;
++criteria_count;
break;
case 'v':
opt_negate = 1;
break;
case 'x':
opt_exact = 1;
break;
case 'd':
opt_delim = strdup (optarg);
break;
case 'P':
opt_ppid = split_list (optarg, ',', conv_num);
if (opt_ppid == NULL)
usage (opt);
++criteria_count;
break;
case 'g':
opt_pgrp = split_list (optarg, ',', conv_pgrp);
if (opt_pgrp == NULL)
usage (opt);
break;
case 's':
opt_sid = split_list (optarg, ',', conv_sid);
if (opt_sid == NULL)
usage (opt);
++criteria_count;
break;
case 'u':
opt_euid = split_list (optarg, ',', conv_uid);
if (opt_euid == NULL)
usage (opt);
++criteria_count;
break;
case 'U':
opt_uid = split_list (optarg, ',', conv_uid);
if (opt_uid == NULL)
usage (opt);
++criteria_count;
break;
case 'G':
opt_gid = split_list (optarg, ',', conv_gid);
if (opt_gid == NULL)
usage (opt);
++criteria_count;
break;
case 't':
opt_term = split_list (optarg, ',', conv_str);
if (opt_term == NULL)
usage (opt);
++criteria_count;
break;
case '?':
usage (opt);
break;
}
}
if (argc - optind == 1)
opt_pattern = argv[optind];
else if (argc - optind > 1)
usage (0);
else if (criteria_count == 0) {
fprintf (stderr, "%s: No matching criteria specified\n",
progname);
usage (0);
}
}
static union el *
split_list (const char *str, char sep, int (*convert)(const char *, union el *))
{
char *copy = strdup (str);
char *ptr = copy;
char *sep_pos;
int i = 1, size = 32;
union el *list;
list = malloc (size * sizeof (union el));
if (list == NULL)
exit (3);
do {
sep_pos = strchr (ptr, sep);
if (sep_pos)
*sep_pos = 0;
if (convert (ptr, &list[i]))
++i;
else
exit (2);
if (i == size) {
size *= 2;
list = realloc (list, size * sizeof (union el));
if (list == NULL)
exit (3);
}
if (sep_pos)
ptr = sep_pos + 1;
} while (sep_pos);
free (copy);
if (i == 1) {
free (list);
list = NULL;
} else {
list[0].num = i - 1;
}
return (list);
}
/* strict_atol returns a Boolean: TRUE if the input string contains a
plain number, FALSE if there are any non-digits. */
static int
strict_atol (const char *str, long *value)
{
int res = 0;
int sign = 1;
if (*str == '+')
++str;
else if (*str == '-') {
++str;
sign = -1;
}
for ( ; *str; ++str) {
if (! isdigit (*str))
return (0);
res *= 10;
res += *str - '0';
}
*value = sign * res;
return (1);
}
static int
conv_uid (const char *name, union el *e)
{
struct passwd *pwd;
if (strict_atol (name, &e->num))
return (1);
pwd = getpwnam (name);
if (pwd == NULL) {
fprintf (stderr, "%s: invalid user name: %s\n",
progname, name);
return (0);
}
e->num = pwd->pw_uid;
return (1);
}
static int
conv_gid (const char *name, union el *e)
{
struct group *grp;
if (strict_atol (name, &e->num))
return (1);
grp = getgrnam (name);
if (grp == NULL) {
fprintf (stderr, "%s: invalid group name: %s\n",
progname, name);
return (0);
}
e->num = grp->gr_gid;
return (1);
}
static int
conv_pgrp (const char *name, union el *e)
{
if (! strict_atol (name, &e->num)) {
fprintf (stderr, "%s: invalid process group: %s\n",
progname, name);
return (0);
}
if (e->num == 0)
e->num = getpgrp ();
return (1);
}
static int
conv_sid (const char *name, union el *e)
{
if (! strict_atol (name, &e->num)) {
fprintf (stderr, "%s: invalid session id: %s\n",
progname, name);
return (0);
}
if (e->num == 0)
e->num = getsid (0);
return (1);
}
static int
conv_num (const char *name, union el *e)
{
if (! strict_atol (name, &e->num)) {
fprintf (stderr, "%s: not a number: %s\n",
progname, name);
return (0);
}
return (1);
}
static int
conv_str (const char *name, union el *e)
{
e->str = strdup (name);
return (1);
}
static int
match_numlist (long value, const union el *list)
{
int found = 0;
if (list == NULL)
found = 0;
else {
int i;
for (i = list[0].num; i > 0; i--) {
if (list[i].num == value)
found = 1;
}
}
return (found);
}
static int
match_strlist (const char *value, const union el *list)
{
int found = 0;
if (list == NULL)
found = 0;
else {
int i;
for (i = list[0].num; i > 0; i--) {
if (! strcmp (list[i].str, value))
found = 1;
}
}
return (found);
}
static void
output_numlist (const union el *list)
{
int i;
for (i = 1; i < list[0].num; i++)
printf ("%ld%s", list[i].num, opt_delim);
if (list[0].num)
printf ("%ld\n", list[i].num);
}
static void
output_strlist (const union el *list)
{
int i;
for (i = 1; i < list[0].num; i++)
printf ("%s%s", list[i].str, opt_delim);
if (list[0].num)
printf ("%s\n", list[i].str);
}
static PROCTAB *
do_openproc ()
{
PROCTAB *ptp;
int flags = PROC_FILLANY;
if (opt_pattern || opt_full)
flags |= PROC_FILLCMD;
if (opt_uid)
flags |= PROC_FILLSTATUS;
if (opt_euid && !opt_negate) {
int num = opt_euid[0].num;
int i = num;
uid_t *uids = malloc (num * sizeof (uid_t));
if (uids == NULL)
exit (3);
while (i-- > 0) {
uids[i] = opt_euid[i+1].num;
}
flags |= PROC_UID;
ptp = openproc (flags, uids, num);
} else {
ptp = openproc (flags);
}
return (ptp);
}
static regex_t *
do_regcomp ()
{
regex_t *preg = NULL;
if (opt_pattern) {
char *re;
char errbuf[256];
int re_err;
preg = malloc (sizeof (regex_t));
if (preg == NULL)
exit (3);
if (opt_exact) {
re = malloc (strlen (opt_pattern) + 5);
if (re == NULL)
exit (3);
sprintf (re, "^(%s)$", opt_pattern);
} else {
re = opt_pattern;
}
re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB);
if (re_err) {
regerror (re_err, preg, errbuf, sizeof(errbuf));
fprintf (stderr, errbuf);
exit (2);
}
}
return preg;
}
#ifdef NOT_USED
static time_t
jiffies_to_time_t (long jiffies)
{
static time_t time_of_boot = 0;
if (time_of_boot == 0) {
time_of_boot = time (NULL) - uptime (0, 0);
}
return (time_of_boot + jiffies / Hertz);
}
#endif
static union el *
select_procs ()
{
PROCTAB *ptp;
proc_t task;
long newest_start_time = 0;
pid_t newest_pid = 0;
int matches = 0;
int size = 32;
regex_t *preg;
pid_t myself = getpid();
union el *list;
char cmd[4096];
list = malloc (size * sizeof (union el));
if (list == NULL)
exit (3);
ptp = do_openproc ();
preg = do_regcomp ();
memset (&task, 0, sizeof (task));
while (readproc (ptp, &task)) {
int match = 1;
if (task.pid == myself)
continue;
else if (opt_newest && task.start_time < newest_start_time)
match = 0;
else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid))
match = 0;
else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp))
match = 0;
else if (opt_euid && ! match_numlist (task.euid, opt_euid))
match = 0;
else if (opt_uid && ! match_numlist (task.ruid, opt_uid))
match = 0;
else if (opt_gid && ! match_numlist (task.rgid, opt_gid))
match = 0;
else if (opt_sid && ! match_numlist (task.session, opt_sid))
match = 0;
else if (opt_term) {
if (task.tty == -1) {
match = 0;
} else {
char tty[256];
dev_to_tty (tty, sizeof(tty) - 1,
task.tty, task.pid, ABBREV_DEV);
match = match_strlist (tty, opt_term);
}
}
if (opt_long || (match && opt_pattern)) {
if (opt_full && task.cmdline) {
int i = 0;
int bytes = sizeof (cmd) - 1;
/* make sure it is always NUL-terminated */
cmd[bytes] = 0;
/* make room for SPC in loop below */
--bytes;
strncpy (cmd, task.cmdline[i], bytes);
bytes -= strlen (task.cmdline[i++]);
while (task.cmdline[i] && bytes > 0) {
strncat (cmd, " ", bytes);
strncat (cmd, task.cmdline[i], bytes);
bytes -= strlen (task.cmdline[i++]) + 1;
}
} else {
strcpy (cmd, task.cmd);
}
}
if (match && opt_pattern) {
if (regexec (preg, cmd, 0, NULL, 0) != 0)
match = 0;
}
if (match ^ opt_negate) { /* Exclusive OR is neat */
if (opt_newest) {
if (newest_start_time == task.start_time &&
newest_pid > task.pid)
continue;
newest_start_time = task.start_time;
newest_pid = task.pid;
matches = 0;
}
if (opt_long) {
char buff[4096];
sprintf (buff, "%d %s", task.pid, cmd);
list[++matches].str = strdup (buff);
} else {
list[++matches].num = task.pid;
}
if (matches == size) {
size *= 2;
list = realloc (list,
size * sizeof (union el));
if (list == NULL)
exit (3);
}
}
memset (&task, 0, sizeof (task));
}
closeproc (ptp);
list[0].num = matches;
return (list);
}
int
main (int argc, char **argv)
{
union el *procs;
parse_opts (argc, argv);
procs = select_procs ();
if (i_am_pkill) {
int i;
for (i = 1; i <= procs[0].num; i++) {
if (kill (procs[i].num, opt_signal) == -1)
fprintf (stderr, "pkill: %ld - %s\n",
procs[i].num, strerror (errno));
}
} else {
if (opt_long)
output_strlist (procs);
else
output_numlist (procs);
}
return ((procs[0].num) == 0 ? 1 : 0);
}

1
pkill.1 Normal file
View File

@ -0,0 +1 @@
.so man1/pgrep.1

34
pmap.c Normal file
View File

@ -0,0 +1,34 @@
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void usage(void){
fprintf(stderr,
"Usage: pmap [-r] [-x] pid...\n"
"-r ignored (compatibility option)\n"
"-x show details\n"
);
exit(1);
}
int one_proc(unsigned pid){
char cmdbuf[64];
char buf[32];
int fd;
sprintf(buf,"/proc/%u/cmdline",pid);
if( ((fd=open(buf)) == -1) return 1;
count = read(fd, cmdbuf, sizeof(cmdbuf)-1);
if(count<1) return 1;
cmdbuf[count] = '\0';
while(count--) if(!isprint(cmdbuf[count])) cmdbuf[count]=' ';
close(fd);
sprintf(buf,"/proc/%u/maps",pid);
if( ((fd=open(buf)) == -1) return 1;
printf("%u: %s\n", pid, cmdbuf);
if(x_option)
printf("Address kB Resident Shared Private Permissions Name\n");
@@@ FIXME FIXME FIXME @@@
}
int main(int argc, char *argv[]){
}

7
proc/.cvsignore.patch Normal file
View File

@ -0,0 +1,7 @@
diff -Naur procps-2.0.6/proc/.cvsignore procps-2.0.7/proc/.cvsignore
--- procps-2.0.6/proc/.cvsignore Wed Dec 31 19:00:00 1969
+++ procps-2.0.7/proc/.cvsignore Fri Jul 14 16:45:01 2000
@@ -0,0 +1,3 @@
+.depend
+signames.h
+libproc.so.*

481
proc/COPYING Normal file
View File

@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<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!

98
proc/Makefile Normal file
View File

@ -0,0 +1,98 @@
# Auto-adaptive C library Makefile adapted for libproc, Chuck Blake.
# Assumptions are basically that all the .c files in the CWD are modules
# for the library and that all .h files are the interface to the library.
# PROJECT SPECIFIC MACROS
NAME = proc
# INSTALLATION OPTIONS
TOPDIR = /usr
HDRDIR = $(TOPDIR)/include/$(NAME)# where to put .h files
LIBDIR = $(TOPDIR)/lib# where to put library files
SHLIBDIR = /lib# where to put shared library files
HDROWN = $(OWNERGROUP) # owner of header files
LIBOWN = $(OWNERGROUP) # owner of library files
INSTALL = install
# ----------------------------------------------------------------#
# The rest is the auto-magic section -- highly GNU make dependent #
# You should never need to edit this. #
# ----------------------------------------------------------------#
VC_SUF = ,v
VC_PFX = RCS/
RCSFILES = $(patsubst $(VC_PFX)%$(VC_SUF),%,$(wildcard $(VC_PFX)*$(VC_SUF)))
# We take the union of RCS files and other files in CWD so that new files do
# not need to alter this makefile. 'sort' removes duplicates. This allows the
# convenience of compiling and testing new files before the initial check-in.
SRC = $(sort $(wildcard *.c) $(filter %.c,$(RCSFILES)))
HDR = $(sort $(wildcard *.h) $(filter %.h,$(RCSFILES)))
OBJ = $(SRC:.c=.o)
SONAME = lib$(NAME).so.$(LIBVERSION)
ifeq ($(SHARED),1)
CFLAGS += -fpic
all: lib$(NAME).a $(SONAME)
else
all: lib$(NAME).a
endif
lib$(NAME).a: $(OBJ)
$(AR) rcs $@ $^
$(SONAME): $(OBJ)
gcc -shared -Wl,-soname,$(SONAME) -o $@ $^ -lc
ln -sf $(SONAME) lib$(NAME).so
# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT
.depend:
$(strip $(CC) $(CFLAGS) -MM -MG $(SRC) > .depend)
-include .depend
# INSTALLATION
install: all
if ! [ -d $(HDRDIR) ] ; then mkdir $(HDRDIR) ; fi
$(INSTALL) $(HDROWN) $(HDR) $(TOPDIR)/include/$(NAME)
$(INSTALL) $(LIBOWN) lib$(NAME).a $(LIBDIR)
ifeq ($(SHARED),1)
$(INSTALL) $(LIBOWN) $(SONAME) $(SHLIBDIR)
cd $(SHLIBDIR) && ln -sf $(SONAME) lib$(NAME).so
ldconfig
endif
# VARIOUS SHORT CUT TARGETS
.PHONY: all install dep clean distclean checkout checkclean
dep: .depend
clean:
$(RM) lib$(NAME).* *.o DEADJOE
distclean: clean
$(RM) .depend *~
checkout:
$(CO) $(RCSFILES)
checkclean:
$(RM) $(RCSFILES)
# CUSTOM c -> o rule so that command-line has minimal whitespace
%.o : %.c
$(strip $(CC) $(CFLAGS) -c $<)
# PROJECT SPECIFIC DEPENDENCIES/BUILD RULES
version.o: version.c version.h
ifdef MINORVERSION
$(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c version.c)
else
$(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -c version.c)
endif

49
proc/alloc.c Normal file
View File

@ -0,0 +1,49 @@
/***********************************************************************\
* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
* *
* This file is placed under the conditions of the GNU Library *
* General Public License, version 2, or any later version. *
* See file COPYING for information on distribution conditions. *
\***********************************************************************/
#include <stdlib.h>
#include <stdio.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);
} else {
return ret;
}
}
void *xmalloc(unsigned int size) {
void *p;
if (size == 0)
++size;
p = malloc(size);
if (!p) {
fprintf(stderr, "xmalloc: malloc(%d) failed", size);
perror(NULL);
exit(1);
}
return(p);
}
void *xrealloc(void *oldp, unsigned int size) {
void *p;
if (size == 0)
++size;
p = realloc(oldp, size);
if (!p) {
fprintf(stderr, "xrealloc: realloc(%d) failed", size);
perror(NULL);
exit(1);
}
return(p);
}

304
proc/compare.c Normal file
View File

@ -0,0 +1,304 @@
/*
*
* Copyright 1994 Charles Blake and Michael K. Johnson
* This file is a part of procps, which is distributable
* under the conditions of the GNU Library General Public License.
* See the file COPYING for details.
*
*/
#include <string.h> /* for strcmp */
#include <stdio.h> /* for parse error output */
#include "proc/readproc.h" /* for proc_t */
#include "proc/tree.h" /* for struct tree_node */
#include "proc/compare.h" /* for this code */
/*
This module was written by Charles Blake for procps.
mult_lvl_cmp:
slick general purpose multi-level compare function I invented.
sort_depth:
the number of levels of functions *to use*. This means many more levels
can be defined than mult_lvl_cmp tres out. If this is 1 then mult_lvl_cmp
is just a trivial wrapper around (*sort_function[0]).
sort_direction:
multiplicative factor for the output of cmp_whatever.
1 ==> default order, -1 ==> reverse order, 0 ==> forced equality
The 0 bit is the neat part. Since a value of zero is the code for equality
multiplying the output of cmp_foo(a,b) forces a==b to be true. This is a
convenient way to turn sorting off in middle levels of a multi-level sort.
If time is a problem, reforming the whole sort_function array to not include
these unsorted middle levels will be faster since then cmp_foo won't even
be called. It might simplify some code depending upon how you organize it.
sort_function[]:
array of function pointers that points to our family of comparison functions
(I have named them cmp_* but mult_lvl_cmp doesn't care what they're named).
This may be declared and initialized like so:
int (*sort_function[])(void* a, void* b)={&cmp_foo, &cmp_bar, &cmp_hiho};
You could also use my command line '-O' parser below.
Note that we only descend levels until the order is determined. If we descend
all levels, that means that the items are equal at all levels, so we return 0.
Otherwise we return whatever the level's cmp_foo function would have returned.
This allows whatever default behavior you want for cmp_foo. sort_direction[]
reverses this default behavior, but mult_lvl_cmp doesn't decide that ascending
or descending is the default. That is the job of your cmp_foo's.
*/
/* the only reason these are global is because qsort(3) likes it that way.
It's also a little more efficient if mult_lvl_cmp() is called many times.
*/
static int sort_depth = 0;
static int sort_direction[10]; /* storage for 10 levels, but 4 would be plenty!*/
static int (*sort_function[10])(void* a, void* b);
int mult_lvl_cmp(void* a, void* b) {
int i, cmp_val;
for(i = 0; i < sort_depth; i++) {
cmp_val = sort_direction[i] * (*sort_function[i])(a,b);
if (cmp_val != 0)
return cmp_val;
}
return 0;
}
int node_mult_lvl_cmp(void* a, void* b) {
int i, cmp_val;
for(i = 0; i < sort_depth; i++) {
cmp_val = sort_direction[i] * (*sort_function[i])(&(((struct tree_node *)a)->proc),&(((struct tree_node *)b)->proc));
if (cmp_val != 0)
return cmp_val;
}
return 0;
}
/* qsort(3) compliant comparison functions for all members of the ps_proc
structure (in the same order in which they appear in the proc_t declaration)
return is {-1,0,1} as {a<b, a==b, a>b}
default ordering is ascending for all members. (flip 1,-1 to reverse)
*/
/* pre-processor macros to cut down on source size (and typing!)
Note the use of the string concatenation operator ##
*/
#define CMP_STR(NAME) \
static int cmp_ ## NAME(proc_t** P, proc_t** Q) { \
return strcmp((*P)->NAME, (*Q)->NAME); \
}
#define CMP_INT(NAME) \
static int cmp_ ## NAME (proc_t** P, proc_t** Q) { \
if ((*P)->NAME < (*Q)->NAME) return -1; \
if ((*P)->NAME > (*Q)->NAME) return 1; \
return 0; \
}
/* Define the (46!) cmp_ functions with the above macros for every element
of proc_t. If the binary gets too big, we could nuke inessentials.
*/
/* CMP_STR(cmdline) */
CMP_STR(ruser)
CMP_STR(euser)
CMP_STR(cmd)
/* CMP_INT(state) */
/* CMP_STR(ttyc) */
CMP_INT(euid)
CMP_INT(pid)
CMP_INT(ppid)
CMP_INT(pgrp)
CMP_INT(session)
CMP_INT(tty)
CMP_INT(tpgid)
CMP_INT(utime)
CMP_INT(stime)
CMP_INT(cutime)
CMP_INT(cstime)
/* CMP_INT(priority) */
CMP_INT(nice)
CMP_INT(start_time)
/* CMP_INT(signal) */
/* CMP_INT(blocked) */
/* CMP_INT(sigignore) */
/* CMP_INT(sigcatch) */
CMP_INT(flags)
CMP_INT(min_flt)
CMP_INT(cmin_flt)
CMP_INT(maj_flt)
CMP_INT(cmaj_flt)
/* CMP_INT(timeout) */
CMP_INT(vsize)
CMP_INT(rss)
/* CMP_INT(rss_rlim) */
/* CMP_INT(start_code) */
/* CMP_INT(end_code) */
/* CMP_INT(start_stack) */
/* CMP_INT(kstk_esp) */
/* CMP_INT(kstk_eip) */
/* CMP_INT(wchan) */
CMP_INT(pcpu)
CMP_INT(size)
CMP_INT(resident)
CMP_INT(share)
/* CMP_INT(trs) */
/* CMP_INT(lrs) */
/* CMP_INT(drs) */
/* CMP_INT(dt) */
/* define user interface to sort keys. Fairly self-explanatory. */
static struct cmp_fun_struct {
char letter; /* single option-letter for key */
char name[15]; /* long option name for key */
int (*fun)(proc_t**, proc_t**); /* pointer to cmp_key */
} cmp[] = {
/* { '?', "cmdline", &cmp_cmdline }, */
{ 'u', "user", &cmp_euser },
/* { '?', "ruser", &cmp_ruser }, */
{ 'c', "cmd", &cmp_cmd },
/* { '?', "state", &cmp_state }, */
/* { '?', "ttyc", &cmp_ttyc }, */
{ 'U', "uid", &cmp_euid },
{ 'p', "pid", &cmp_pid },
{ 'P', "ppid", &cmp_ppid },
{ 'g', "pgrp", &cmp_pgrp },
{ 'o', "session", &cmp_session },
{ 't', "tty", &cmp_tty },
{ 'G', "tpgid", &cmp_tpgid },
{ 'k', "utime", &cmp_utime },
{ 'K', "stime", &cmp_stime },
{ 'j', "cutime", &cmp_cutime },
{ 'J', "cstime", &cmp_cstime },
/* { '?', "counter", &cmp_counter }, */
{ 'y', "priority", &cmp_nice },
{ 'T', "start_time", &cmp_start_time },
/* { '?', "signal", &cmp_signal }, */
/* { '?', "blocked", &cmp_blocked }, */
/* { '?', "sigignore", &cmp_sigignore }, */
/* { '?', "sigcatch", &cmp_sigcatch }, */
{ 'f', "flags", &cmp_flags },
{ 'm', "min_flt", &cmp_min_flt },
{ 'n', "cmin_flt", &cmp_cmin_flt },
{ 'M', "maj_flt", &cmp_maj_flt },
{ 'N', "cmaj_flt", &cmp_cmaj_flt },
/* { 'C', "timeout", &cmp_timeout }, */
{ 'v', "vsize", &cmp_vsize },
{ 'r', "rss", &cmp_rss },
/* { '?', "rss_rlim", &cmp_rss_rlim }, */
/* { '?', "start_code", &cmp_start_code }, */
/* { '?', "end_code", &cmp_end_code }, */
/* { '?', "start_stack", &cmp_start_stack }, */
/* { '?', "kstk_esp", &cmp_kstk_esp }, */
/* { '?', "kstk_eip", &cmp_kstk_eip }, */
/* { '?', "wchan", &cmp_wchan }, */
{ 'C', "pcpu", &cmp_pcpu },
{ 's', "size", &cmp_size },
{ 'R', "resident", &cmp_resident },
{ 'S', "share", &cmp_share },
/* { '?', "trs", &cmp_trs }, */
/* { '?', "lrs", &cmp_lrs }, */
/* { '?', "drs", &cmp_drs }, */
/* { '?', "dt", &cmp_dt }, */
{ '\0',"terminator", NULL }
};
/* command line option parsing. Assign sort_{depth,direction[],function[]}
based upon a string of the form:
[+-]a[+-]b[+-]c...
with a,b,c,... being letter flags corresponding to a particular sort
key and the optional '-' specifying a reverse sort on that key. + doesn't
mean anything, but it keeps things looking balanced...
*/
const char *parse_sort_opt(const char* opt) {
int i, next_dir=1;
for(; *opt ; ++opt) {
if (*opt == '-' || *opt == '+') {
if (*opt == '-')
next_dir = -1;
opt++;
continue;
}
for (i = 0; cmp[i].letter; i++)
if (*opt == cmp[i].letter)
break;
if (!cmp[i].letter) { /* failed, clear and return */
sort_depth=0;
for (i=0;i<10;i++){
sort_direction[i]=0;
sort_function[i]=(cmp_t)NULL;
}
return "Unknown sort key.";
} else {
#ifdef DEBUG
fprintf(stderr,
"sort level %d: key %s, direction % d\n",
sort_depth, cmp[i].name, next_dir);
#endif
sort_function[sort_depth] = (cmp_t)cmp[i].fun;
sort_direction[sort_depth++] = next_dir;
next_dir = 1;
}
}
return NULL;
}
const char *parse_long_sort(const char* opt) {
char* comma;
int i, more_keys, next_dir=1;
do {
if (*opt == '-' || *opt == '+') {
if (*opt == '-')
next_dir = -1;
more_keys = 1;
opt++;
continue;
}
more_keys = ((comma=index(opt,',')) != NULL);
/* keys are ',' delimited */
if (more_keys)
*comma='\0'; /* terminate for strcmp() */
for(i = 0; cmp[i].letter; ++i)
if (strcmp(opt, cmp[i].name) == 0)
break;
if (!cmp[i].letter) { /* failed, clear and return */
sort_depth=0;
for (i=0;i<10;i++){
sort_direction[i]=0;
sort_function[i]=(cmp_t)NULL;
}
return "Unknown sort key.";
} else {
#ifdef DEBUG
fprintf(stderr,
"sort level %d: key %s, direction % d\n",
sort_depth, cmp[i].name, next_dir);
#endif
sort_function[sort_depth] = (cmp_t)cmp[i].fun;
sort_direction[sort_depth++] = next_dir;
next_dir = 1;
}
opt = comma + 1; /* do next loop on next key, if more keys, else done*/
} while (more_keys);
return NULL;
}
void reset_sort_options (void)
{
int i;
sort_depth=0;
for (i=0;i<10;i++){
sort_direction[i]=0;
sort_function[i]=(cmp_t)NULL;
}
}
void register_sort_function (int dir, cmp_t func)
{
sort_function[sort_depth] = func;
sort_direction[sort_depth++] = dir;
}

9
proc/compare.h Normal file
View File

@ -0,0 +1,9 @@
typedef int (*cmp_t)(void*,void*); /* for function pointer casts */
extern void register_sort_function (int dir, cmp_t func);
extern void reset_sort_options(void);
extern int mult_lvl_cmp(void* a, void* b);
extern int node_mult_lvl_cmp(void* a, void* b);
extern const char *parse_sort_opt(const char* opt);
extern const char *parse_long_sort(const char* opt);

211
proc/devname.c Normal file
View File

@ -0,0 +1,211 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include "devname.h"
#include <asm/page.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE (sizeof(long)*1024)
#endif
/* Who uses what:
*
* tty_to_dev oldps, w (there is a fancy version in ps)
* dev_to_tty oldps, top, ps
*/
typedef struct tty_map_node {
struct tty_map_node *next;
int major_number; /* not unsigned! Ugh... */
char name[4];
} tty_map_node;
static tty_map_node *tty_map = NULL;
/* Load /proc/tty/drivers for device name mapping use. */
static void load_drivers(void){
char buf[10000];
char *p;
int fd;
int bytes;
fd = open("/proc/tty/drivers",O_RDONLY);
if(fd == -1) goto fail;
bytes = read(fd, buf, 9999);
if(bytes == -1) goto fail;
buf[bytes] = '\0';
p = buf;
while(( p = strstr(p, " /dev/tty") )){
tty_map_node *tmn;
int len;
p += 9;
len = strspn(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
if(!len) continue;
if(len>3) continue;
if((len=1) && (*p=='S')) continue;
tmn = malloc(sizeof(tty_map_node));
tmn->next = tty_map;
tty_map = tmn;
memset(tmn->name, '\0', 4);
strncpy(tmn->name, p, len);
p += len;
tmn->major_number = atoi(p);
}
fail:
if(fd != -1) close(fd);
if(!tty_map) tty_map = (tty_map_node *)-1;
}
/* Try to guess the device name from /proc/tty/drivers info. */
static int driver_name(char * const buf, int maj, int min){
struct stat sbuf;
tty_map_node *tmn;
if(!tty_map) load_drivers();
if(tty_map == (tty_map_node *)-1) return 0;
tmn = tty_map;
for(;;){
if(!tmn) return 0;
if(tmn->major_number == maj) break;
tmn = tmn->next;
}
sprintf(buf, "/dev/tty%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */
if(stat(buf, &sbuf) < 0) return 0;
if(min != minor(sbuf.st_rdev)) return 0;
if(maj != major(sbuf.st_rdev)) return 0;
return 1;
}
/* Try to guess the device name (useful until /proc/PID/tty is added) */
static int guess_name(char * const buf, int maj, int min){
struct stat sbuf;
int t0, t1;
int tmpmin = min;
switch(maj){
case 4:
if(min<64){
sprintf(buf, "/dev/tty%d", min);
break;
}
if(min<128){ /* to 255 on newer systems */
sprintf(buf, "/dev/ttyS%d", min-64);
break;
}
tmpmin = min & 0x3f; /* FALL THROUGH */
case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
t1 = "0123456789abcdef"[tmpmin&0x0f];
sprintf(buf, "/dev/tty%c%c", t0, t1);
break;
case 17: sprintf(buf, "/dev/ttyH%d", min); break;
case 19: sprintf(buf, "/dev/ttyC%d", min); break;
case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */
case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */
case 24: sprintf(buf, "/dev/ttyE%d", min); break;
case 32: sprintf(buf, "/dev/ttyX%d", min); break;
case 43: sprintf(buf, "/dev/ttyI%d", min); break;
case 46: sprintf(buf, "/dev/ttyR%d", min); break;
case 48: sprintf(buf, "/dev/ttyL%d", min); break;
case 57: sprintf(buf, "/dev/ttyP%d", min); break;
case 71: sprintf(buf, "/dev/ttyF%d", min); break;
case 75: sprintf(buf, "/dev/ttyW%d", min); break;
case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
case 105: sprintf(buf, "/dev/ttyV%d", min); break;
case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
/* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break;
case 148: sprintf(buf, "/dev/ttyT%d", min); break;
case 154: sprintf(buf, "/dev/ttySR%d", min); break;
case 156: sprintf(buf, "/dev/ttySR%d", min+256); break;
case 164: sprintf(buf, "/dev/ttyCH%d", min); break;
case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
case 172: sprintf(buf, "/dev/ttyMX%d", min); break;
case 174: sprintf(buf, "/dev/ttySI%d", min); break;
case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
default: return 0;
}
if(stat(buf, &sbuf) < 0) return 0;
if(min != minor(sbuf.st_rdev)) return 0;
if(maj != major(sbuf.st_rdev)) return 0;
return 1;
}
/* Linux 2.2 can give us filenames that might be correct.
* Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
* and in /proc/PID/fd/255 (used by bash to remember the tty).
*/
static int link_name(char * const buf, int maj, int min, int pid, char *name){
struct stat sbuf;
char path[32];
int count;
sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */
count = readlink(path,buf,PAGE_SIZE-1);
if(count == -1) return 0;
buf[count] = '\0';
if(stat(buf, &sbuf) < 0) return 0;
if(min != minor(sbuf.st_rdev)) return 0;
if(maj != major(sbuf.st_rdev)) return 0;
return 1;
}
/* number --> name */
int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags) {
static char buf[PAGE_SIZE];
char *tmp = buf;
int i = 0;
int c;
if((short)dev == (short)-1) goto fail;
if( link_name(tmp, major(dev), minor(dev), pid, "tty" )) goto abbrev;
if( link_name(tmp, major(dev), minor(dev), pid, "fd/2" )) goto abbrev;
if( guess_name(tmp, major(dev), minor(dev) )) goto abbrev;
if( link_name(tmp, major(dev), minor(dev), pid, "fd/255")) goto abbrev;
if(driver_name(tmp, major(dev), minor(dev) )) goto abbrev;
fail:
strcpy(ret, "?");
return 1;
abbrev:
if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3;
if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
tmp[chop] = '\0';
for(;;){
c = *tmp;
tmp++;
if(!c) break;
i++;
if(c<=' ') c = '?';
if(c>126) c = '?';
*ret = c;
ret++;
}
*ret = '\0';
return i;
}
/* name --> number */
int tty_to_dev(char *name) {
struct stat sbuf;
static char buf[32];
if(stat(name, &sbuf) >= 0) return sbuf.st_rdev;
snprintf(buf,32,"/dev/%s",name);
if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
snprintf(buf,32,"/dev/tty%s",name);
if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
snprintf(buf,32,"/dev/pts/%s",name);
if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
return -1;
}

7
proc/devname.h Normal file
View File

@ -0,0 +1,7 @@
#define ABBREV_DEV 1 /* remove /dev/ */
#define ABBREV_TTY 2 /* remove tty */
#define ABBREV_PTS 4 /* remove pts/ */
int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags);
int tty_to_dev(char *name);

548
proc/ksym.c Normal file
View File

@ -0,0 +1,548 @@
/*
* Copyright 1998 by Albert Cahalan; all rights reserved.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <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 "proc/procps.h"
#include "proc/version.h"
#include "proc/sysinfo.h" /* smp_num_cpus */
#define KSYMS_FILENAME "/proc/ksyms"
#if 0
#undef KSYMS_FILENAME
#define KSYMS_FILENAME "/would/be/nice/to/have/this/file"
#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked"
#define linux_version_code 131598 /* ? */
#define smp_num_cpus 2
#endif
#if 0
#undef KSYMS_FILENAME
#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12"
#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12"
#define linux_version_code 131852 /* 2.3.12 */
#define smp_num_cpus 2
#endif
#if 0
#undef KSYMS_FILENAME
#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS"
#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS"
#define linux_version_code 131858 /* 2.3.18ac8 */
#define smp_num_cpus 2
#endif
#if 0
#undef KSYMS_FILENAME
#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS"
#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS"
#define linux_version_code 131858 /* 2.3.18ac8 */
#define smp_num_cpus 2
#endif
/* These are the symbol types, with relative popularity:
* ? w machine type junk for Alpha -- odd syntax
* ? S not for i386
* 4 W not for i386
* 60 R
* 100 A
* 125 r
* 363 s not for i386
* 858 B
* 905 g generated by modutils?
* 929 G generated by modutils?
* 1301 b
* 2750 D
* 4481 d
* 11417 ?
* 13666 t
* 15442 T
*
* For i386, that is: "RArBbDd?tT"
*/
#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS"
/*
* '?' is a symbol type
* '.' is part of a name (versioning?)
* "\t[]" are for the module name in /proc/ksyms
*/
#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \
"abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
/* System.map lines look like:
* hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n
*
* Alpha systems can start with a few lines that have the address replaced
* by space padding and a 'w' for the type. For those lines, the last space
* is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv
* (just one of those, always with a "_mv", then the newline)
*
* The /proc/ksyms lines are like System.map lines w/o the symbol type char.
* When odd features are used, the name part contains:
* "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}"
* It is likely that more crap will be added...
*/
typedef struct symb {
const char *name;
unsigned long addr;
} symb;
static const symb fail = { "?", 0 };
static const char *dash = "-";
/* These mostly rely on POSIX to make them zero. */
static const symb hashtable[256];
static char *sysmap_data;
static unsigned sysmap_room;
static symb *sysmap_index;
static unsigned sysmap_count;
static char *ksyms_data;
static unsigned ksyms_room = 4096;
static symb *ksyms_index;
static unsigned ksyms_count;
static int idx_room;
/*********************************/
/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$
* We kill: (_R[^A-Z]*[0-9a-f]{8,})+$
*
* The loop should almost never be taken, but it has to be there.
* It gets rid of anything that _looks_ like a version code, even
* if a real version code has already been found. This is because
* the inability to perfectly recognize a version code may lead to
* symbol mangling, which in turn leads to mismatches between the
* /proc/ksyms and System.map data files.
*/
#if 0
static void chop_version(char *arg){
char *cp;
cp = strchr(arg,'\t');
if(cp) *cp = '\0'; /* kill trailing module name first */
for(;;){
char *p;
int len = 0;
cp = strrchr(arg, 'R');
if(!cp || cp<=arg+1 || cp[-1]!='_') break;
for(p=cp; *++p; ){
switch(*p){
default:
return;
case '0' ... '9':
case 'a' ... 'f':
len++;
continue;
case 'g' ... 'z':
case '_':
len=0;
continue;
}
}
if(len<8) break;
cp[-1] = '\0';
}
}
#endif
static void chop_version(char *arg){
char *cp;
cp = strchr(arg,'\t');
if(cp) *cp = '\0'; /* kill trailing module name first */
for(;;){
int len;
cp = strrchr(arg, 'R');
if(!cp || cp<=arg+1 || cp[-1]!='_') break;
len=strlen(cp);
if(len<9) break;
if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break;
if(strspn(cp+len-8,"0123456789abcdef")!=8) break;
cp[-1] = '\0';
}
}
/***********************************/
static const symb *search(unsigned long address, symb *idx, unsigned count){
unsigned left;
unsigned mid;
unsigned right;
if(!idx) return NULL; /* maybe not allocated */
if(address < idx[0].addr) return NULL;
if(address >= idx[count-1].addr) return idx+count-1;
left = 0;
right = count-1;
for(;;){
mid = (left + right) / 2;
if(address >= idx[mid].addr) left = mid;
if(address <= idx[mid].addr) right = mid;
if(right-left <= 1) break;
}
if(address == idx[right].addr) return idx+right;
return idx+left;
}
/*********************************/
/* allocate if needed, read, and return buffer size */
static void read_file(const char *filename, char **bufp, unsigned *roomp) {
int fd = 0;
ssize_t done;
char *buf;
ssize_t total = 0;
unsigned room = *roomp;
buf = *bufp;
if(!room) goto hell; /* failed before */
if(!buf) buf = malloc(room);
if(!buf) goto hell;
open_again:
fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
if(fd<0){
switch(errno){
case EINTR: goto open_again;
default: _exit(101);
case EACCES: /* somebody screwing around? */
/* FIXME: set a flag to disable symbol lookup? */
case ENOENT: /* no module support */
}
goto hell;
}
for(;;){
done = read(fd, buf+total, room-total-1);
if(done==0) break; /* nothing left */
if(done==-1){
if(errno==EINTR) continue; /* try again */
perror("");
goto hell;
}
if(done==(ssize_t)room-total-1){
char *tmp;
total += done;
/* more to go, but no room in buffer */
room *= 2;
tmp = realloc(buf, room);
if(!tmp) goto hell;
buf = tmp;
continue;
}
if(done>0 && done<(ssize_t)room-total-1){
total += done;
continue; /* OK, we read some. Go do more. */
}
fprintf(stderr,"%ld can't happen\n", (long)done);
/* FIXME: memory leak */
_exit(42);
}
*bufp = buf;
*roomp = room;
close(fd);
return;
hell:
if(buf) free(buf);
*bufp = NULL;
*roomp = 0; /* this function will never work again */
total = 0;
close(fd);
return;
}
/*********************************/
static int parse_ksyms(void) {
char *endp;
if(!ksyms_room || !ksyms_data) goto quiet_goodbye;
endp = ksyms_data;
ksyms_count = 0;
if(idx_room) goto bypass; /* some space already allocated */
idx_room = 512;
for(;;){
void *vp;
idx_room *= 2;
vp = realloc(ksyms_index, sizeof(symb)*idx_room);
if(!vp) goto bad_alloc;
ksyms_index = vp;
bypass:
for(;;){
char *saved;
if(!*endp) return 1;
saved = endp;
ksyms_index[ksyms_count].addr = strtoul(endp, &endp, 16);
if(endp==saved || *endp != ' ') goto bad_parse;
endp++;
ksyms_index[ksyms_count].name = endp;
saved = endp;
endp = strchr(endp,'\n');
if(!endp) goto bad_parse; /* no newline */
*endp = '\0';
chop_version(saved);
++endp;
if(++ksyms_count >= idx_room) break; /* need more space */
}
}
if(0){
bad_alloc:
fprintf(stderr, "Warning: not enough memory available\n");
}
if(0){
bad_parse:
fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n");
}
quiet_goodbye:
idx_room = 0;
if(ksyms_data) free(ksyms_data) , ksyms_data = NULL;
ksyms_room = 0;
if(ksyms_index) free(ksyms_index) , ksyms_index = NULL;
ksyms_count = 0;
return 0;
}
/*********************************/
#define VCNT 16
static int sysmap_mmap(const char *filename, void (*message)(const char *, ...)) {
struct stat sbuf;
char *endp;
int fd;
char Version[32];
fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
if(fd<0) return 0;
if(fstat(fd, &sbuf) < 0) goto bad_open;
if(!S_ISREG(sbuf.st_mode)) goto bad_open;
if(sbuf.st_size < 5000) goto bad_open; /* if way too small */
/* Would be shared read-only, but we want '\0' after each name. */
endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
sysmap_data = endp;
while(*endp==' '){ /* damn Alpha machine types */
if(strncmp(endp," w ", 19)) goto bad_parse;
endp += 19;
endp = strchr(endp,'\n');
if(!endp) goto bad_parse; /* no newline */
if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse;
endp++;
}
if(sysmap_data == (caddr_t) -1) goto bad_open;
close(fd);
fd = -1;
sprintf(Version, "Version_%d", linux_version_code);
sysmap_room = 512;
for(;;){
void *vp;
sysmap_room *= 2;
vp = realloc(sysmap_index, sizeof(symb)*sysmap_room);
if(!vp) goto bad_alloc;
sysmap_index = vp;
for(;;){
char *vstart;
if(!*endp){ /* if we reached the end */
int i = VCNT; /* check VCNT times to verify this file */
if(*Version) goto bad_version;
if(!ksyms_index) return 1; /* if can not verify, assume success */
while(i--){
#if 1
const symb *findme;
const symb *map_symb;
/* Choose VCNT entries from /proc/ksyms to test */
findme = ksyms_index + (ksyms_count*i/VCNT);
/* Search for them in the System.map */
map_symb = search(findme->addr, sysmap_index, sysmap_count);
if(map_symb){
if(map_symb->addr != findme->addr) continue;
/* backup to first matching address */
while (map_symb != sysmap_index){
if (map_symb->addr != (map_symb-1)->addr) break;
map_symb--;
}
/* search for name in symbols with same address */
while (map_symb != (sysmap_index+sysmap_count)){
if (map_symb->addr != findme->addr) break;
if (!strcmp(map_symb->name,findme->name)) goto good_match;
map_symb++;
}
map_symb--; /* backup to last symbol with matching address */
message("{%s} {%s}\n",map_symb->name,findme->name);
goto bad_match;
}
good_match:;
#endif
}
return 1; /* success */
}
sysmap_index[sysmap_count].addr = strtoul(endp, &endp, 16);
if(*endp != ' ') goto bad_parse;
endp++;
if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse;
endp++;
if(*endp != ' ') goto bad_parse;
endp++;
sysmap_index[sysmap_count].name = endp;
vstart = endp;
endp = strchr(endp,'\n');
if(!endp) goto bad_parse; /* no newline */
*endp = '\0';
++endp;
chop_version(vstart);
if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0';
if(++sysmap_count >= sysmap_room) break; /* need more space */
}
}
if(0){
bad_match:
message("Warning: %s does not match kernel data.\n", filename);
}
if(0){
bad_version:
message("Warning: %s has an incorrect kernel version.\n", filename);
}
if(0){
bad_alloc:
message("Warning: not enough memory available\n");
}
if(0){
bad_parse:
message("Warning: %s not parseable as a System.map\n", filename);
}
if(0){
bad_open:
message("Warning: %s could not be opened as a System.map\n", filename);
}
sysmap_room=0;
sysmap_count=0;
if(sysmap_index) free(sysmap_index);
sysmap_index = NULL;
if(fd>=0) close(fd);
if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1);
sysmap_data = NULL;
return 0;
}
/*********************************/
static void read_and_parse(void){
static time_t stamp; /* after data gets old, load /proc/ksyms again */
if(time(NULL) != stamp){
read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room);
parse_ksyms();
memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */
stamp = time(NULL);
}
}
/*********************************/
static void default_message(const char *format, ...) {
va_list arg;
va_start (arg, format);
vfprintf (stderr, format, arg);
va_end (arg);
}
/*********************************/
int open_psdb_message(const char *override, void (*message)(const char *, ...)) {
static const char *sysmap_paths[] = {
"/boot/System.map-%s",
"/boot/System.map",
"/lib/modules/%s/System.map",
"/usr/src/linux/System.map",
"/System.map",
NULL
};
struct utsname uts;
char path[64];
const char **fmt = sysmap_paths;
const char *env;
read_and_parse();
#ifdef SYSMAP_FILENAME /* debug feature */
override = SYSMAP_FILENAME;
#endif
if(override){ /* ought to search some path */
if(sysmap_mmap(override, message)) return 0;
return -1; /* ought to return "Namelist not found." */
/* failure is better than ignoring the user & using bad data */
}
/* Arrrgh, the old man page and code did not match. */
if ((env = getenv("PS_SYSMAP")) && sysmap_mmap(env, message)) return 0;
if ((env = getenv("PS_SYSTEM_MAP")) && sysmap_mmap(env, message)) return 0;
uname(&uts);
do{
snprintf(path, sizeof path, *fmt, uts.release);
if (sysmap_mmap(path, message)) return 0;
}while(*++fmt);
/* TODO: Without System.map, no need to keep ksyms loaded. */
return -1;
}
/***************************************/
int open_psdb(const char *override) {
return open_psdb_message(override, default_message);
}
/***************************************/
#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */
/* return pointer to temporary static buffer with function name */
const char * wchan(unsigned long address) {
const symb *mod_symb;
const symb *map_symb;
const symb *good_symb;
const char *ret;
unsigned hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */
if(!address) return dash;
read_and_parse();
if(hashtable[hash].addr == address) return hashtable[hash].name;
mod_symb = search(address, ksyms_index, ksyms_count);
if(!mod_symb) mod_symb = &fail;
map_symb = search(address, sysmap_index, sysmap_count);
if(!map_symb) map_symb = &fail;
/* which result is closest? */
good_symb = (mod_symb->addr > map_symb->addr)
? mod_symb
: map_symb
;
if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail;
/* good_symb->name has the data, but needs to be trimmed */
ret = good_symb->name;
switch(*ret){
case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break;
case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break;
case '_': while(*ret=='_') ret++; break;
}
/* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */
/* cache name after abbreviation */
hashtable[hash].addr = address;
hashtable[hash].name = ret;
return ret;
}

52
proc/output.c Normal file
View File

@ -0,0 +1,52 @@
/*
Some output conversion routines for libproc
Copyright (C) 1996, Charles Blake. See COPYING for details.
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
/* output a string, converting unprintables to octal as we go, and stopping after
processing max chars of output (accounting for expansion due to octal rep).
*/
unsigned print_str(FILE* file, char *s, unsigned max) {
int i;
for (i=0; s[i] && i < max; i++)
if (isprint(s[i]) || s[i] == ' ')
fputc(s[i], file);
else {
if (max - i > 3) {
fprintf(file, "\\%03o", s[i]);
i += 3; /* 4 printed, but i counts one */
} else
return max - i;
}
return max - i;
}
/* output an argv style NULL-terminated string list, converting unprintables
to octal as we go, separating items of the list by 'sep' and stopping after
processing max chars of output (accounting for expansion due to octal rep).
*/
unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max) {
int i, n, seplen = strlen(sep);
for (n=0; *strs && n < max; strs++) {
for (i=0; strs[0][i] && n+i < max; i++)
if (isprint(strs[0][i]) || strs[0][i] == ' ')
fputc(strs[0][i], file);
else {
if (max-(n+i) > 3) {
fprintf(file, "\\%03o", strs[0][i]);
n += 3; /* 4 printed, but i counts one */
} else
return max - n;
}
n += i;
if (n + seplen < max) {
fputs(sep, file);
n += seplen;
} else
return max - n;
}
return max - n;
}

27
proc/procps.h Normal file
View File

@ -0,0 +1,27 @@
/* The shadow of the original with only common prototypes now. */
#include <stdio.h>
#include <sys/types.h>
/* The HZ constant from <asm/param.h> is replaced by the Hertz variable
* available from "proc/sysinfo.h".
*/
/* get page info */
#include <asm/page.h>
void *xrealloc(void *oldp, unsigned int size);
void *xmalloc(unsigned int size);
void *xcalloc(void *pointer, int size);
int mult_lvl_cmp(void* a, void* b);
int node_mult_lvl_cmp(void* a, void* b);
char *user_from_uid(uid_t uid);
char *group_from_gid(gid_t gid);
const char * wchan(unsigned long address);
int open_psdb(const char *override);
int open_psdb_message(const char *override, void (*message)(const char *, ...));
unsigned print_str (FILE* file, char *s, unsigned max);
unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max);

74
proc/pwcache.c Normal file
View File

@ -0,0 +1,74 @@
/***********************************************************************\
* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
* *
* This file is placed under the conditions of the GNU Library *
* General Public License, version 2, or any later version. *
* See file ../COPYING for information on distribution conditions. *
\***********************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pwd.h>
#include "proc/procps.h"
#include <grp.h>
#define HASHSIZE 16 /* power of 2 */
#define HASH(x) ((x) & (HASHSIZE - 1))
#define NAMESIZE 16
#define NAMELENGTH "15"
static struct pwbuf {
uid_t uid;
char name[NAMESIZE];
struct pwbuf *next;
} *pwhash[HASHSIZE];
char *user_from_uid(uid_t uid)
{
struct pwbuf **p;
struct passwd *pw;
p = &pwhash[HASH(uid)];
while (*p) {
if ((*p)->uid == uid)
return((*p)->name);
p = &(*p)->next;
}
*p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf));
(*p)->uid = uid;
if ((pw = getpwuid(uid)) == NULL)
sprintf((*p)->name, "#%d", uid);
else
sprintf((*p)->name, "%-." NAMELENGTH "s", pw->pw_name);
(*p)->next = NULL;
return((*p)->name);
}
static struct grpbuf {
gid_t gid;
char name[NAMESIZE];
struct grpbuf *next;
} *grphash[HASHSIZE];
char *group_from_gid(gid_t gid)
{
struct grpbuf **g;
struct group *gr;
g = &grphash[HASH(gid)];
while (*g) {
if ((*g)->gid == gid)
return((*g)->name);
g = &(*g)->next;
}
*g = (struct grpbuf *) malloc(sizeof(struct grpbuf));
(*g)->gid = gid;
if ((gr = getgrgid(gid)) == NULL)
sprintf((*g)->name, "#%d", gid);
else
sprintf((*g)->name, "%-." NAMELENGTH "s", gr->gr_name);
(*g)->next = NULL;
return((*g)->name);
}

543
proc/readproc.c Normal file
View File

@ -0,0 +1,543 @@
/*
* New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
* Copyright (C) 1996 Charles L. Blake.
* Copyright (C) 1998 Michael K. Johnson
* May be distributed under the conditions of the
* GNU Library General Public License; a copy is in COPYING
*/
#include "proc/version.h"
#include "proc/readproc.h"
#include "proc/devname.h"
#include "proc/procps.h"
#include <stdio.h>
#include <stdlib.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>
#define Do(x) (flags & PROC_ ## x) /* convenient shorthand */
/* initiate a process table scan
*/
PROCTAB* openproc(int flags, ...) {
va_list ap;
PROCTAB* PT = xmalloc(sizeof(PROCTAB));
if (Do(PID))
PT->procfs = NULL;
else if (!(PT->procfs = opendir("/proc")))
return NULL;
PT->flags = flags;
va_start(ap, flags); /* Init args list */
if (Do(PID))
PT->pids = va_arg(ap, pid_t*);
else if (Do(TTY))
PT->ttys = va_arg(ap, dev_t*);
else if (Do(UID)) {
PT->uids = va_arg(ap, uid_t*);
PT->nuid = va_arg(ap, int);
} else if (Do(STAT))
PT->stats = va_arg(ap, char*);
va_end(ap); /* Clean up args list */
if (Do(ANYTTY) && Do(TTY))
PT->flags = PT->flags & ~PROC_TTY; /* turn off TTY flag */
return PT;
}
/* terminate a process table scan
*/
void closeproc(PROCTAB* PT) {
if (PT){
if (PT->procfs) closedir(PT->procfs);
free(PT);
}
}
/* deallocate the space allocated by readproc if the passed rbuf was NULL
*/
void freeproc(proc_t* p) {
if (!p) /* in case p is NULL */
return;
/* ptrs are after strings to avoid copying memory when building them. */
/* so free is called on the address of the address of strvec[0]. */
if (p->cmdline)
free((void*)*p->cmdline);
if (p->environ)
free((void*)*p->environ);
free(p);
}
static void status2proc (char* S, proc_t* P, int fill) {
char* tmp;
if (fill == 1) {
memset(P->cmd, 0, sizeof P->cmd);
sscanf (S, "Name:\t%15c", P->cmd);
tmp = strchr(P->cmd,'\n');
*tmp='\0';
tmp = strstr (S,"State");
sscanf (tmp, "State:\t%c", &P->state);
}
tmp = strstr (S,"Pid:");
if(tmp) sscanf (tmp,
"Pid:\t%d\n"
"PPid:\t%d\n",
&P->pid,
&P->ppid
);
else fprintf(stderr, "Internal error!\n");
tmp = strstr (S,"Uid:");
if(tmp) sscanf (tmp,
"Uid:\t%d\t%d\t%d\t%d",
&P->ruid, &P->euid, &P->suid, &P->fuid
);
else fprintf(stderr, "Internal error!\n");
tmp = strstr (S,"Gid:");
if(tmp) sscanf (tmp,
"Gid:\t%d\t%d\t%d\t%d",
&P->rgid, &P->egid, &P->sgid, &P->fgid
);
else fprintf(stderr, "Internal error!\n");
tmp = strstr (S,"VmSize:");
if(tmp) sscanf (tmp,
"VmSize: %lu kB\n"
"VmLck: %lu kB\n"
"VmRSS: %lu kB\n"
"VmData: %lu kB\n"
"VmStk: %lu kB\n"
"VmExe: %lu kB\n"
"VmLib: %lu kB\n",
&P->vm_size, &P->vm_lock, &P->vm_rss, &P->vm_data,
&P->vm_stack, &P->vm_exe, &P->vm_lib
);
else /* looks like an annoying kernel thread */
{
P->vm_size = 0;
P->vm_lock = 0;
P->vm_rss = 0;
P->vm_data = 0;
P->vm_stack = 0;
P->vm_exe = 0;
P->vm_lib = 0;
}
tmp = strstr (S,"SigPnd:");
if(tmp) sscanf (tmp,
#ifdef SIGNAL_STRING
"SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
P->signal, P->blocked, P->sigignore, P->sigcatch
#else
"SigPnd: %Lx SigBlk: %Lx SigIgn: %Lx %*s %Lx",
&P->signal, &P->blocked, &P->sigignore, &P->sigcatch
#endif
);
else fprintf(stderr, "Internal error!\n");
}
/* stat2proc() makes sure it can handle arbitrary executable file basenames
* for `cmd', i.e. those with embedded whitespace or embedded ')'s.
* Such names confuse %s (see scanf(3)), so the string is split and %39c
* is used instead. (except for embedded ')' "(%[^)]c)" would work.
*/
static void stat2proc(char* S, proc_t* P) {
int num;
char* tmp = strrchr(S, ')'); /* split into "PID (cmd" and "<rest>" */
*tmp = '\0'; /* replace trailing ')' with NUL */
/* fill in default values for older kernels */
P->exit_signal = SIGCHLD;
P->processor = 0;
/* parse these two strings separately, skipping the leading "(". */
memset(P->cmd, 0, sizeof P->cmd); /* clear even though *P xcalloc'd ?! */
sscanf(S, "%d (%15c", &P->pid, P->cmd); /* comm[16] in kernel */
num = sscanf(tmp + 2, /* skip space after ')' too */
"%c "
"%d %d %d %d %d "
"%lu %lu %lu %lu %lu %lu %lu "
"%ld %ld %ld %ld %ld %ld "
"%lu %lu "
"%ld "
"%lu %lu %lu %lu %lu %lu "
"%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
"%lu %lu %lu "
"%d %d",
&P->state,
&P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
&P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, &P->utime, &P->stime,
&P->cutime, &P->cstime, &P->priority, &P->nice, &P->timeout, &P->it_real_value,
&P->start_time, &P->vsize,
&P->rss,
&P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */
&P->wchan, &P->nswap, &P->cnswap,
/* -- Linux 2.0.35 ends here -- */
&P->exit_signal, &P->processor /* 2.2.1 ends with "exit_signal" */
/* -- Linux 2.2.8 and 2.3.47 end here -- */
);
/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
if (P->tty == 0)
P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 */
}
static void statm2proc(char* s, proc_t* P) {
int num;
num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
&P->size, &P->resident, &P->share,
&P->trs, &P->lrs, &P->drs, &P->dt);
/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */
}
static int file2str(char *directory, char *what, char *ret, int cap) {
static char filename[80];
int fd, num_read;
sprintf(filename, "%s/%s", directory, what);
if ( (fd = open(filename, O_RDONLY, 0)) == -1 ) return -1;
if ( (num_read = read(fd, ret, cap - 1)) <= 0 ) num_read = -1;
else ret[num_read] = 0;
close(fd);
return num_read;
}
static char** file2strvec(char* directory, char* what) {
char buf[2048]; /* read buf bytes at a time */
char *p, *rbuf = 0, *endbuf, **q, **ret;
int fd, tot = 0, n, c, end_of_file = 0;
int align;
sprintf(buf, "%s/%s", directory, what);
if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return NULL;
/* read whole file into a memory buffer, allocating as we go */
while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
if (n < sizeof buf - 1)
end_of_file = 1;
if (n == 0 && rbuf == 0)
return NULL; /* process died between our open and read */
if (n < 0) {
if (rbuf)
free(rbuf);
return NULL; /* read error */
}
if (end_of_file && buf[n-1]) /* last read char not null */
buf[n++] = '\0'; /* so append null-terminator */
rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */
memcpy(rbuf + tot, buf, n); /* copy buffer into it */
tot += n; /* increment total byte ctr */
if (end_of_file)
break;
}
close(fd);
if (n <= 0 && !end_of_file) {
if (rbuf) free(rbuf);
return NULL; /* read error */
}
endbuf = rbuf + tot; /* count space for pointers */
align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
for (c = 0, p = rbuf; p < endbuf; p++)
if (!*p)
c += sizeof(char*);
c += sizeof(char*); /* one extra for NULL term */
rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */
endbuf = rbuf + tot; /* addr just past data buf */
q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
*q++ = p = rbuf; /* point ptrs to the strings */
endbuf--; /* do not traverse final NUL */
while (++p < endbuf)
if (!*p) /* NUL char implies that */
*q++ = p+1; /* next string -> next char */
*q = 0; /* null ptr list terminator */
return ret;
}
/* These are some nice GNU C expression subscope "inline" functions.
* The can be used with arbitrary types and evaluate their arguments
* exactly once.
*/
/* Test if item X of type T is present in the 0 terminated list L */
# define XinL(T, X, L) ( { \
T x = (X), *l = (L); \
while (*l && *l != x) l++; \
*l == x; \
} )
/* Test if item X of type T is present in the list L of length N */
# define XinLN(T, X, L, N) ( { \
T x = (X), *l = (L); \
int i = 0, n = (N); \
while (i < n && l[i] != x) i++; \
i < n && l[i] == x; \
} )
/* readproc: return a pointer to a proc_t filled with requested info about the
* next process available matching the restriction set. If no more such
* processes are available, return a null pointer (boolean false). Use the
* passed buffer instead of allocating space if it is non-NULL. */
/* This is optimized so that if a PID list is given, only those files are
* searched for in /proc. If other lists are given in addition to the PID list,
* the same logic can follow through as for the no-PID list case. This is
* fairly complex, but it does try to not to do any unnecessary work.
* Unfortunately, the reverse filtering option in which any PID *except* the
* ones listed is pursued.
*/
proc_t* readproc(PROCTAB* PT, proc_t* rbuf) {
static struct direct *ent; /* dirent handle */
static struct stat sb; /* stat buffer */
static char path[32], sbuf[512]; /* bufs for stat,statm */
int allocated = 0, matched = 0; /* flags */
proc_t *p = NULL;
/* loop until a proc matching restrictions is found or no more processes */
/* I know this could be a while loop -- this way is easier to indent ;-) */
next_proc: /* get next PID for consideration */
/*printf("PT->flags is 0x%08x\n", PT->flags);*/
#define flags (PT->flags)
if (Do(PID)) {
if (!*PT->pids) /* set to next item in pids */
return NULL;
sprintf(path, "/proc/%d", *(PT->pids)++);
matched = 1;
} else { /* get next numeric /proc ent */
while ((ent = readdir(PT->procfs)) &&
(*ent->d_name < '0' || *ent->d_name > '9'))
;
if (!ent || !ent->d_name)
return NULL;
sprintf(path, "/proc/%s", ent->d_name);
}
if (stat(path, &sb) == -1) /* no such dirent (anymore) */
goto next_proc;
if (Do(UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
goto next_proc; /* not one of the requested uids */
if (!allocated) { /* assign mem for return buf */
p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */
allocated = 1; /* remember space is set up */
}
p->euid = sb.st_uid; /* need a way to get real uid */
if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1)
goto next_proc; /* error reading /proc/#/stat */
stat2proc(sbuf, p); /* parse /proc/#/stat */
if (!matched && Do(TTY) && !XinL(dev_t, p->tty, PT->ttys))
goto next_proc; /* not one of the requested ttys */
if (!matched && Do(ANYTTY) && p->tty == -1)
goto next_proc; /* no controlling terminal */
if (!matched && Do(STAT) && !strchr(PT->stats,p->state))
goto next_proc; /* not one of the requested states */
if (Do(FILLMEM)) { /* read, parse /proc/#/statm */
if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 )
statm2proc(sbuf, p); /* ignore statm errors here */
} /* statm fields just zero */
if (Do(FILLSTATUS)) { /* read, parse /proc/#/status */
if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){
status2proc(sbuf, p, 0 /*FIXME*/);
}
}
/* some number->text resolving which is time consuming */
if (Do(FILLUSR)){
strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
if(Do(FILLSTATUS)) {
strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
}
}
if (Do(FILLCMD)) /* read+parse /proc/#/cmdline */
p->cmdline = file2strvec(path, "cmdline");
if (Do(FILLENV)) /* read+parse /proc/#/environ */
p->environ = file2strvec(path, "environ");
if (p->state == 'Z') /* fixup cmd for zombies */
strncat(p->cmd," <defunct>", sizeof p->cmd);
return p;
}
#undef flags
/* ps_readproc: return a pointer to a proc_t filled with requested info about the
* next process available matching the restriction set. If no more such
* processes are available, return a null pointer (boolean false). Use the
* passed buffer instead of allocating space if it is non-NULL. */
/* This is optimized so that if a PID list is given, only those files are
* searched for in /proc. If other lists are given in addition to the PID list,
* the same logic can follow through as for the no-PID list case. This is
* fairly complex, but it does try to not to do any unnecessary work.
* Unfortunately, the reverse filtering option in which any PID *except* the
* ones listed is pursued.
*/
proc_t* ps_readproc(PROCTAB* PT, proc_t* rbuf) {
static struct direct *ent; /* dirent handle */
static struct stat sb; /* stat buffer */
static char path[32], sbuf[512]; /* bufs for stat,statm */
int allocated = 0 /* , matched = 0 */ ; /* flags */
proc_t *p = NULL;
/* loop until a proc matching restrictions is found or no more processes */
/* I know this could be a while loop -- this way is easier to indent ;-) */
next_proc: /* get next PID for consideration */
/*printf("PT->flags is 0x%08x\n", PT->flags);*/
#define flags (PT->flags)
while ((ent = readdir(PT->procfs)) &&
(*ent->d_name < '0' || *ent->d_name > '9'))
;
if (!ent || !ent->d_name)
return NULL;
sprintf(path, "/proc/%s", ent->d_name);
if (stat(path, &sb) == -1) /* no such dirent (anymore) */
goto next_proc;
if (!allocated) { /* assign mem for return buf */
p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */
allocated = 1; /* remember space is set up */
}
p->euid = sb.st_uid; /* need a way to get real uid */
if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1)
goto next_proc; /* error reading /proc/#/stat */
stat2proc(sbuf, p); /* parse /proc/#/stat */
/* if (Do(FILLMEM)) {*/ /* read, parse /proc/#/statm */
if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 )
statm2proc(sbuf, p); /* ignore statm errors here */
/* } */ /* statm fields just zero */
/* if (Do(FILLSTATUS)) { */ /* read, parse /proc/#/status */
if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){
status2proc(sbuf, p, 0 /*FIXME*/);
}
/* }*/
/* some number->text resolving which is time consuming */
/* if (Do(FILLUSR)){ */
strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
/* if(Do(FILLSTATUS)) { */
strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
/* }*/
/* }*/
/* if (Do(FILLCMD)) */ /* read+parse /proc/#/cmdline */
p->cmdline = file2strvec(path, "cmdline");
/* if (Do(FILLENV)) */ /* read+parse /proc/#/environ */
p->environ = file2strvec(path, "environ");
if (p->state == 'Z') /* fixup cmd for zombies */
strncat(p->cmd," <defunct>", sizeof p->cmd);
return p;
}
#undef flags
void look_up_our_self(proc_t *p) {
static char path[32], sbuf[512]; /* bufs for stat,statm */
sprintf(path, "/proc/%d", getpid());
file2str(path, "stat", sbuf, sizeof sbuf);
stat2proc(sbuf, p); /* parse /proc/#/stat */
file2str(path, "statm", sbuf, sizeof sbuf);
statm2proc(sbuf, p); /* ignore statm errors here */
file2str(path, "status", sbuf, sizeof sbuf);
status2proc(sbuf, p, 0 /*FIXME*/);
}
/* Convenient wrapper around openproc and readproc to slurp in the whole process
* tree subset satisfying the constraints of flags and the optional PID list.
* Free allocated memory with freeproctree(). The tree structure is a classic
* left-list children + right-list siblings. The algorithm is a two-pass of the
* process table. Since most process trees will have children with strictly
* increasing PIDs, most of the structure will be picked up in the first pass.
* The second loop then cleans up any nodes which turn out to have preceeded
* their parent in /proc order.
*/
/* Traverse tree 't' breadth-first looking for a process with pid p */
static proc_t* LookupPID(proc_t* t, pid_t p) {
proc_t* tmp = NULL;
if (!t)
return NULL;
if (t->pid == p) /* look here/terminate recursion */
return t;
if ((tmp = LookupPID(t->l, p))) /* recurse over children */
return tmp;
for (; t; t=t->r) /* recurse over siblings */
if ((tmp = LookupPID(tmp, p)))
return tmp;
return NULL;
}
/* Convenient wrapper around openproc and readproc to slurp in the whole process
* table subset satisfying the constraints of flags and the optional PID list.
* Free allocated memory with freeproctab(). Access via tab[N]->member. The
* pointer list is NULL terminated.
*/
proc_t** readproctab(int flags, ...) {
PROCTAB* PT = NULL;
proc_t** tab = NULL;
int n = 0;
va_list ap;
va_start(ap, flags); /* pass through args to openproc */
if (Do(UID)) {
/* temporary variables to ensure that va_arg() instances
* are called in the right order
*/
uid_t* u;
int i;
u = va_arg(ap, uid_t*);
i = va_arg(ap, int);
PT = openproc(flags, u, i);
}
else if (Do(PID) || Do(TTY) || Do(STAT))
PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
else
PT = openproc(flags);
va_end(ap);
do { /* read table: */
tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
tab[n] = readproc(PT, NULL); /* final null to terminate */
} while (tab[n++]); /* stop when NULL reached */
closeproc(PT);
return tab;
}

200
proc/readproc.h Normal file
View File

@ -0,0 +1,200 @@
#ifndef PROCPS_PROC_READPROC_H
#define PROCPS_PROC_READPROC_H
/*
* New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
* Copyright (C) 1996 Charles L. Blake.
* Copyright (C) 1998 Michael K. Johnson
* May be distributed under the terms of the
* GNU Library General Public License, a copy of which is provided
* in the file COPYING
*/
#define SIGNAL_STRING
/*
ld cutime, cstime, priority, nice, timeout, it_real_value, rss,
c state,
d ppid, pgrp, session, tty, tpgid,
s signal, blocked, sigignore, sigcatch,
lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime,
lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip,
lu start_time, vsize, wchan, nswap, cnswap,
*/
/* Basic data structure which holds all information we can get about a process.
* (unless otherwise specified, fields are read from /proc/#/stat)
*
* Most of it comes from task_struct in linux/sched.h
*/
typedef struct proc_s {
#ifdef SIGNAL_STRING
char
/* Linux 2.1.7x and up have more signals. This handles 88. */
signal[24], /* mask of pending signals */
blocked[24], /* mask of blocked signals */
sigignore[24], /* mask of ignored signals */
sigcatch[24]; /* mask of caught signals */
#else
long long
/* Linux 2.1.7x and up have more signals. This handles 64. */
signal, /* mask of pending signals */
blocked, /* mask of blocked signals */
sigignore, /* mask of ignored signals */
sigcatch; /* mask of caught signals */
#endif
long
cutime, /* cumulative utime of process and reaped children */
cstime, /* cumulative stime of process and reaped children */
priority, /* kernel scheduling priority */
timeout, /* ? */
nice, /* standard unix nice level of process */
rss, /* resident set size from /proc/#/stat (pages) */
it_real_value, /* ? */
/* the next 7 members come from /proc/#/statm */
size, /* total # of pages of memory */
resident, /* number of resident set (non-swapped) pages (4k) */
share, /* number of pages of shared (mmap'd) memory */
trs, /* text resident set size */
lrs, /* shared-lib resident set size */
drs, /* data resident set size */
dt; /* dirty pages */
unsigned long
/* FIXME: are these longs? Maybe when the alpha does PCI bounce buffers */
vm_size, /* same as vsize in kb */
vm_lock, /* locked pages in kb */
vm_rss, /* same as rss in kb */
vm_data, /* data size */
vm_stack, /* stack size */
vm_exe, /* executable size */
vm_lib, /* library size (all pages, not just used ones) */
vsize, /* number of pages of virtual memory ... */
rss_rlim, /* resident set size limit? */
flags, /* kernel flags for the process */
min_flt, /* number of minor page faults since process start */
maj_flt, /* number of major page faults since process start */
cmin_flt, /* cumulative min_flt of process and child processes */
cmaj_flt, /* cumulative maj_flt of process and child processes */
nswap, /* ? */
cnswap, /* cumulative nswap ? */
utime, /* user-mode CPU time accumulated by process */
stime, /* kernel-mode CPU time accumulated by process */
start_code, /* address of beginning of code segment */
end_code, /* address of end of code segment */
start_stack, /* address of the bottom of stack for the process */
kstk_esp, /* kernel stack pointer */
kstk_eip, /* kernel instruction pointer */
start_time, /* start time of process -- seconds since 1-1-70 */
wchan; /* address of kernel wait channel proc is sleeping in */
struct proc_s *l, /* ptrs for building arbitrary linked structs */
*r; /* (i.e. singly/doubly-linked lists and trees */
char
**environ, /* environment string vector (/proc/#/environ) */
**cmdline; /* command line string vector (/proc/#/cmdline) */
char
/* Be compatible: Digital allows 16 and NT allows 14 ??? */
ruser[16], /* real user name */
euser[16], /* effective user name */
suser[16], /* saved user name */
fuser[16], /* filesystem user name */
rgroup[16], /* real group name */
egroup[16], /* effective group name */
sgroup[16], /* saved group name */
fgroup[16], /* filesystem group name */
cmd[16]; /* basename of executable file in call to exec(2) */
int
ruid, rgid, /* real */
euid, egid, /* effective */
suid, sgid, /* saved */
fuid, fgid, /* fs (used for file access only) */
pid, /* process id */
ppid, /* pid of parent process */
pgrp, /* process group id */
session, /* session id */
tty, /* full device number of controlling terminal */
tpgid, /* terminal process group id */
exit_signal, /* might not be SIGCHLD */
processor; /* current (or most recent?) CPU */
unsigned
pcpu; /* %CPU usage (is not filled in by readproc!!!) */
char
state; /* single-char code for process state (S=sleeping) */
} proc_t;
/* PROCTAB: data structure holding the persistent information readproc needs
* from openproc(). The setup is intentionally similar to the dirent interface
* and other system table interfaces (utmp+wtmp come to mind).
*/
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
typedef struct {
DIR* procfs;
int flags;
pid_t* pids; /* pids of the procs */
dev_t* ttys; /* devnos of the cttys */
uid_t* uids; /* uids of procs */
int nuid; /* cannot really sentinel-terminate unsigned short[] */
char* stats; /* status chars (actually output into /proc//stat) */
} PROCTAB;
/* initialize a PROCTAB structure holding needed call-to-call persistent data
*/
PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
/* Convenient wrapper around openproc and readproc to slurp in the whole process
* table subset satisfying the constraints of flags and the optional PID list.
* Free allocated memory with freeproctab(). Access via tab[N]->member. The
* pointer list is NULL terminated.
*/
proc_t** readproctab(int flags, ... /* same as openproc */ );
/* clean-up open files, etc from the openproc()
*/
void closeproc(PROCTAB* PT);
/* retrieve the next process matching the criteria set by the openproc()
*/
proc_t* readproc(PROCTAB* PT, proc_t* return_buf);
proc_t* ps_readproc(PROCTAB* PT, proc_t* return_buf);
void look_up_our_self(proc_t *p);
/* deallocate space allocated by readproc
*/
void freeproc(proc_t* p);
/* openproc/readproctab:
*
* Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be
* opened.) By default readproc will consider all processes as valid to parse
* and return, but not actually fill in the cmdline, environ, and /proc/#/statm
* derived memory fields.
*
* `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The
* "fill" options will cause more of the proc_t to be filled in. The "filter"
* options all use the second argument as the pointer to a list of objects:
* process status', process id's, user id's, and tty device numbers. The third
* argument is the length of the list (currently only used for lists of user
* id's since unsigned short[] supports no convenient termination sentinel.)
*/
#define PROC_FILLANY 0x00 /* either stat or status will do */
#define PROC_FILLMEM 0x01 /* read statm into the appropriate proc_t entries */
#define PROC_FILLCMD 0x02 /* alloc and fill in `cmdline' part of proc_t */
#define PROC_FILLENV 0x04 /* alloc and fill in `environ' part of proc_t */
#define PROC_FILLUSR 0x08 /* resolve user id number -> user name via passwd */
#define PROC_FILLSTATUS 0x10
#define PROC_FILLSTAT 0x20
#define PROC_FILLBUG 0x3f /* No idea what we need */
/* Obsolete, consider only processes with one of the passed: */
#define PROC_PID 0x0100 /* process id numbers ( 0 terminated) */
#define PROC_TTY 0x0200 /* ctty device nos. ( 0 terminated) */
#define PROC_UID 0x0400 /* user id numbers ( length needed ) */
#define PROC_STAT 0x0800 /* status fields ('\0' terminated) */
#define PROC_ANYTTY 0x1000 /* proc must have a controlling terminal */
#endif

234
proc/sig.c Normal file
View File

@ -0,0 +1,234 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.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 {
char *name;
int num;
} mapstruct;
static mapstruct sigtable[] = {
{"ABRT", SIGABRT}, /* IOT */
{"ALRM", SIGALRM},
{"BUS", SIGBUS},
{"CHLD", SIGCHLD}, /* CLD */
{"CONT", SIGCONT},
#ifdef SIGEMT
{"EMT", SIGEMT},
#endif
{"FPE", SIGFPE},
{"HUP", SIGHUP},
{"ILL", SIGILL},
{"INT", SIGINT},
{"KILL", SIGKILL},
{"PIPE", SIGPIPE},
{"POLL", SIGPOLL}, /* IO */
{"PROF", SIGPROF},
{"PWR", SIGPWR},
{"QUIT", SIGQUIT},
{"SEGV", SIGSEGV},
#ifdef SIGSTKFLT
{"STKFLT", SIGSTKFLT},
#endif
{"STOP", SIGSTOP},
{"SYS", SIGSYS}, /* UNUSED */
{"TERM", SIGTERM},
{"TRAP", SIGTRAP},
{"TSTP", SIGTSTP},
{"TTIN", SIGTTIN},
{"TTOU", SIGTTOU},
{"URG", SIGURG},
{"USR1", SIGUSR1},
{"USR2", SIGUSR2},
{"VTALRM", SIGVTALRM},
{"WINCH", SIGWINCH},
{"XCPU", SIGXCPU},
{"XFSZ", SIGXFSZ}
};
static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct);
static int compare_signal_names(const void *a, const void *b){
return strcasecmp( ((mapstruct*)a)->name, ((mapstruct*)b)->name );
}
/* return -1 on failure */
int signal_name_to_number(char *name){
const mapstruct *ptr;
mapstruct ms;
long val;
char *endp;
int offset;
/* clean up name */
if(!strncasecmp(name,"SIG",3)) name += 3;
if(!strcasecmp(name,"CLD")) return SIGCHLD;
if(!strcasecmp(name,"IO")) return SIGPOLL;
if(!strcasecmp(name,"IOT")) return SIGABRT;
/* search the table */
ms.name = name;
ptr = bsearch(&ms, sigtable, number_of_signals,
sizeof(mapstruct), compare_signal_names
);
if(ptr) return ptr->num;
if(!strcasecmp(name,"RTMIN")) return SIGRTMIN;
if(!strcasecmp(name,"EXIT")) return 0;
if(!strcasecmp(name,"NULL")) return 0;
offset = 0;
if(!strncasecmp(name,"RTMIN+",6)){
name += 6;
offset = SIGRTMIN;
}
/* not found, so try as a number */
val = strtol(name,&endp,10);
if(*endp) return -1; /* not valid */
if(val+SIGRTMIN>127) return -1; /* not valid */
return val+offset;
}
static const char *signal_number_to_name(int signo){
static char buf[32];
int n = number_of_signals;
signo &= 0x7f; /* need to process exit values too */
while(n--){
if(sigtable[n].num==signo) return sigtable[n].name;
}
if(signo == SIGRTMIN) return "RTMIN";
if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN);
else strcpy(buf,"0"); /* AIX has NULL; Solaris has EXIT */
return buf;
}
int print_given_signals(int argc, char *argv[], int max_line){
char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */
int ret = 0; /* to be used as exit code by caller */
int place = 0; /* position on this line */
int amt;
if(argc > 128) return 1;
while(argc--){
char tmpbuf[16];
char *txt; /* convenient alias */
txt = *argv;
if(*txt >= '0' && *txt <= '9'){
long val;
char *endp;
val = strtol(txt,&endp,10);
if(*endp){
fprintf(stderr, "Signal \"%s\" not known.\n", txt);
ret = 1;
goto end;
}
amt = sprintf(tmpbuf, "%s", signal_number_to_name(val));
}else{
int sno;
sno = signal_name_to_number(txt);
if(sno == -1){
fprintf(stderr, "Signal \"%s\" not known.\n", txt);
ret = 1;
goto end;
}
amt = sprintf(tmpbuf, "%d", sno);
}
if(!place){
strcpy(buf,tmpbuf);
place = amt;
goto end;
}
if(amt+place+1 > max_line){
printf("%s\n", buf);
strcpy(buf,tmpbuf);
place = amt;
goto end;
}
sprintf(buf+place, " %s", tmpbuf);
place += amt+1;
end:
argv++;
}
if(place) printf("%s\n", buf);
return ret;
}
void pretty_print_signals(void){
int i = 0;
while(++i <= number_of_signals){
int n;
n = printf("%2d %s", i, signal_number_to_name(i));
if(i%7) printf(" \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n);
else printf("\n");
}
if((i-1)%7) printf("\n");
}
void unix_print_signals(void){
int pos = 0;
int i = 0;
while(++i <= number_of_signals){
if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') );
pos += printf("%s", signal_number_to_name(i));
}
printf("\n");
}
/* sanity check */
static int init_signal_list(void) __attribute__((constructor));
static int init_signal_list(void){
if(number_of_signals != 31){
fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals);
}
return 0;
}

19
proc/sig.h Normal file
View File

@ -0,0 +1,19 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
/* return -1 on failure */
extern int signal_name_to_number(char *name);
extern int print_given_signals(int argc, char *argv[], int max_line);
extern void pretty_print_signals(void);
extern void unix_print_signals(void);

29
proc/status.c Normal file
View File

@ -0,0 +1,29 @@
/***********************************************************************\
* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
* *
* This file is placed under the conditions of the GNU Library *
* General Public License, version 2, or any later version. *
* See file COPYING for information on distribution conditions. *
\***********************************************************************/
#include "proc/procps.h"
#include "proc/readproc.h"
char * status(proc_t* task) {
static char buf[4] = " ";
buf[0] = task->state;
if (task->rss == 0 && task->state != 'Z')
buf[1] = 'W';
else
buf[1] = ' ';
if (task->nice < 0)
buf[2] = '<';
else if (task->nice > 0)
buf[2] = 'N';
else
buf[2] = ' ';
return(buf);
}

4
proc/status.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef __PROC_STATUS_H
#define __PROC_STATUS_H
extern char *status(proc_t* task);
#endif

325
proc/sysinfo.c Normal file
View File

@ -0,0 +1,325 @@
/***********************************************************************\
* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
* *
* This file is placed under the conditions of the GNU Library *
* General Public License, version 2, or any later version. *
* See file COPYING for information on distribution conditions. *
\***********************************************************************/
/* File for parsing top-level /proc entities. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include "proc/version.h"
#include "proc/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, mount /proc /proc -t proc\n"
#define STAT_FILE "/proc/stat"
static int stat_fd = -1;
#define UPTIME_FILE "/proc/uptime"
static int uptime_fd = -1;
#define LOADAVG_FILE "/proc/loadavg"
static int loadavg_fd = -1;
#define MEMINFO_FILE "/proc/meminfo"
static int meminfo_fd = -1;
static char buf[1024];
/* This macro opens filename only if necessary and seeks to 0 so
* that successive calls to the functions are more efficient.
* It also reads the current contents of the file into the global buf.
*/
#define FILE_TO_BUF(filename, fd) do{ \
static int local_n; \
if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
fprintf(stderr, BAD_OPEN_MESSAGE); \
fflush(NULL); \
_exit(102); \
} \
lseek(fd, 0L, SEEK_SET); \
if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
perror(filename); \
fflush(NULL); \
_exit(103); \
} \
buf[local_n] = '\0'; \
}while(0)
/* evals 'x' twice */
#define SET_IF_DESIRED(x,y) do{ if(x) *(x) = (y); }while(0)
/***********************************************************************/
int uptime(double *uptime_secs, double *idle_secs) {
double up=0, idle=0;
FILE_TO_BUF(UPTIME_FILE,uptime_fd);
if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
fprintf(stderr, "bad data in " UPTIME_FILE "\n");
return 0;
}
SET_IF_DESIRED(uptime_secs, up);
SET_IF_DESIRED(idle_secs, idle);
return up; /* assume never be zero seconds in practice */
}
/***********************************************************************
* Some values in /proc are expressed in units of 1/HZ seconds, where HZ
* is the kernel clock tick rate. One of these units is called a jiffy.
* The HZ value used in the kernel may vary according to hacker desire.
* According to Linus Torvalds, this is not true. He considers the values
* in /proc as being in architecture-dependant units that have no relation
* to the kernel clock tick rate. Examination of the kernel source code
* reveals that opinion as wishful thinking.
*
* In any case, we need the HZ constant as used in /proc. (the real HZ value
* may differ, but we don't care) There are several ways we could get HZ:
*
* 1. Include the kernel header file. If it changes, recompile this library.
* 2. Use the sysconf() function. When HZ changes, recompile the C library!
* 3. Ask the kernel. This is obviously correct...
*
* Linus Torvalds won't let us ask the kernel, because he thinks we should
* not know the HZ value. Oh well, we don't have to listen to him.
* Someone smuggled out the HZ value. :-)
*
* This code should work fine, even if Linus fixes the kernel to match his
* stated behavior. The code only fails in case of a partial conversion.
*
*/
unsigned long Hertz;
static void init_Hertz_value(void) __attribute__((constructor));
static void init_Hertz_value(void){
unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */
double up_1, up_2, seconds;
unsigned long jiffies, h;
smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
if(smp_num_cpus==-1) smp_num_cpus=1;
do{
FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1);
/* uptime(&up_1, NULL); */
FILE_TO_BUF(STAT_FILE,stat_fd);
sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2);
/* uptime(&up_2, NULL); */
} while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
jiffies = user_j + nice_j + sys_j + other_j;
seconds = (up_1 + up_2) / 2;
h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
/* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
switch(h){
case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */
case 48 ... 52 : Hertz = 50; break;
case 58 ... 62 : Hertz = 60; break;
case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */
case 95 ... 105 : Hertz = 100; break; /* normal Linux */
case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */
case 195 ... 204 : Hertz = 200; break; /* normal << 1 */
case 253 ... 260 : Hertz = 256; break;
case 393 ... 408 : Hertz = 400; break; /* normal << 2 */
case 790 ... 808 : Hertz = 800; break; /* normal << 3 */
case 990 ... 1010 : Hertz = 1000; break; /* ARM */
case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */
case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */
default:
#ifdef HZ
Hertz = (unsigned long)HZ; /* <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! (%ld) Assume %ld.\n", h, Hertz);
}
}
/***********************************************************************
* The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we
* recover jiffies by adding up the 4 numbers we are given. SMP kernels
* (as of pre-2.4 era) can report idle time going backwards, perhaps due
* to non-atomic reads and updates. There is no locking for these values.
*/
#ifndef NAN
#define NAN (-0.0)
#endif
#define JT unsigned long
void four_cpu_numbers(double *uret, double *nret, double *sret, double *iret){
double tmp_u, tmp_n, tmp_s, tmp_i;
double scale; /* scale values to % */
static JT old_u, old_n, old_s, old_i;
JT new_u, new_n, new_s, new_i;
JT ticks_past; /* avoid div-by-0 by not calling too often :-( */
FILE_TO_BUF(STAT_FILE,stat_fd);
sscanf(buf, "cpu %lu %lu %lu %lu", &new_u, &new_n, &new_s, &new_i);
ticks_past = (new_u+new_n+new_s+new_i)-(old_u+old_n+old_s+old_i);
if(ticks_past){
scale = 100.0 / (double)ticks_past;
tmp_u = ( (double)new_u - (double)old_u ) * scale;
tmp_n = ( (double)new_n - (double)old_n ) * scale;
tmp_s = ( (double)new_s - (double)old_s ) * scale;
tmp_i = ( (double)new_i - (double)old_i ) * scale;
}else{
tmp_u = NAN;
tmp_n = NAN;
tmp_s = NAN;
tmp_i = NAN;
}
SET_IF_DESIRED(uret, tmp_u);
SET_IF_DESIRED(nret, tmp_n);
SET_IF_DESIRED(sret, tmp_s);
SET_IF_DESIRED(iret, tmp_i);
old_u=new_u;
old_n=new_n;
old_s=new_s;
old_i=new_i;
}
#undef JT
/***********************************************************************/
void loadavg(double *av1, double *av5, double *av15) {
double avg_1=0, avg_5=0, avg_15=0;
FILE_TO_BUF(LOADAVG_FILE,loadavg_fd);
if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
fprintf(stderr, "bad data in " LOADAVG_FILE "\n");
exit(1);
}
SET_IF_DESIRED(av1, avg_1);
SET_IF_DESIRED(av5, avg_5);
SET_IF_DESIRED(av15, avg_15);
}
/***********************************************************************/
/*
* Copyright 1999 by Albert Cahalan; all rights reserved.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
typedef struct mem_table_struct {
const char *name; /* memory type name */
const unsigned *slot; /* slot in return struct */
} mem_table_struct;
static int compare_mem_table_structs(const void *a, const void *b){
return strcmp(((mem_table_struct*)a)->name,((mem_table_struct*)b)->name);
}
/* example data, following junk, with comments added:
*
* MemTotal: 61768 kB old
* MemFree: 1436 kB old
* MemShared: 0 kB old (now always zero; not calculated)
* Buffers: 1312 kB old
* Cached: 20932 kB old
* Active: 12464 kB new
* Inact_dirty: 7772 kB new
* Inact_clean: 2008 kB new
* Inact_target: 0 kB new
* HighTotal: 0 kB
* HighFree: 0 kB
* LowTotal: 61768 kB
* LowFree: 1436 kB
* SwapTotal: 122580 kB old
* SwapFree: 60352 kB old
*/
/* obsolete */
unsigned kb_main_shared;
/* old but still kicking -- the important stuff */
unsigned kb_main_buffers;
unsigned kb_main_cached;
unsigned kb_main_free;
unsigned kb_main_total;
unsigned kb_swap_free;
unsigned kb_swap_total;
/* recently introduced */
unsigned kb_high_free;
unsigned kb_high_total;
unsigned kb_low_free;
unsigned kb_low_total;
/* 2.4.xx era */
unsigned kb_active;
unsigned kb_inact_dirty;
unsigned kb_inact_clean;
unsigned kb_inact_target;
/* derived values */
unsigned kb_swap_used;
unsigned kb_main_used;
void meminfo(void){
char namebuf[16]; /* big enough to hold any row name */
mem_table_struct findme = { namebuf, NULL};
mem_table_struct *found;
char *head;
char *tail;
static const mem_table_struct mem_table[] = {
{"Active", &kb_active},
{"Buffers", &kb_main_buffers},
{"Cached", &kb_main_cached},
{"HighFree", &kb_high_free},
{"HighTotal", &kb_high_total},
{"Inact_clean", &kb_inact_clean},
{"Inact_dirty", &kb_inact_dirty},
{"Inact_target", &kb_inact_target},
{"LowFree", &kb_low_free},
{"LowTotal", &kb_low_total},
{"MemFree", &kb_main_free},
{"MemShared", &kb_main_shared},
{"MemTotal", &kb_main_total},
{"SwapFree", &kb_swap_free},
{"SwapTotal", &kb_swap_total}
};
const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);
FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
head = buf;
for(;;){
tail = strchr(head, ':');
if(!tail) break;
*tail = '\0';
if(strlen(head) >= sizeof(namebuf)){
head = tail+1;
goto nextline;
}
strcpy(namebuf,head);
found = bsearch(&findme, mem_table, mem_table_count,
sizeof(mem_table_struct), compare_mem_table_structs
);
head = tail+1;
if(!found) goto nextline;
*(found->slot) = strtoul(head,&tail,10);
nextline:
tail = strchr(head, '\n');
if(!tail) break;
head = tail+1;
}
if(!kb_low_total){ /* low==main except with large-memory support */
kb_low_total = kb_main_total;
kb_low_free = kb_main_free;
}
kb_swap_used = kb_swap_total - kb_swap_free;
kb_main_used = kb_main_total - kb_main_free;
}

40
proc/sysinfo.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef SYSINFO_H
#define SYSINFO_H
extern unsigned long Hertz; /* clock tick frequency */
extern long smp_num_cpus; /* number of CPUs */
#define JT double
extern void four_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret);
#undef JT
extern int uptime (double *uptime_secs, double *idle_secs);
extern void loadavg(double *av1, double *av5, double *av15);
/* obsolete */
extern unsigned kb_main_shared;
/* old but still kicking -- the important stuff */
extern unsigned kb_main_buffers;
extern unsigned kb_main_cached;
extern unsigned kb_main_free;
extern unsigned kb_main_total;
extern unsigned kb_swap_free;
extern unsigned kb_swap_total;
/* recently introduced */
extern unsigned kb_high_free;
extern unsigned kb_high_total;
extern unsigned kb_low_free;
extern unsigned kb_low_total;
/* 2.4.xx era */
extern unsigned kb_active;
extern unsigned kb_inact_dirty;
extern unsigned kb_inact_clean;
extern unsigned kb_inact_target;
/* derived values */
extern unsigned kb_swap_used;
extern unsigned kb_main_used;
extern void meminfo(void);
#endif /* SYSINFO_H */

15
proc/tree.h Normal file
View File

@ -0,0 +1,15 @@
/* for oldps.c and proc/compare.c */
struct tree_node {
proc_t *proc;
pid_t pid;
pid_t ppid;
char *line;
char *cmd;
char **cmdline;
char **environ;
int children;
int maxchildren;
int *child;
int have_parent;
};

43
proc/version.c Normal file
View File

@ -0,0 +1,43 @@
/* Suite version information for procps utilities
* Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
* Ammended by cblake to only export the function symbol.
* Redistributable under the terms of the
* GNU Library General Public License; see COPYING
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef MINORVERSION
char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION;
#else
char procps_version[] = "procps version " VERSION "." SUBVERSION;
#endif
void display_version(void) {
fprintf(stdout, "%s\n", procps_version);
}
/* Linux kernel version information for procps utilities
* Copyright (c) 1996 Charles Blake <cblake@bbn.com>
*/
#include <sys/utsname.h>
#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
int linux_version_code = 0;
static void init_Linux_version(void) __attribute__((constructor));
static void init_Linux_version(void) {
static struct utsname uts;
int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
if (linux_version_code) return;
if (uname(&uts) == -1) /* failure implies impending death */
exit(1);
if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3)
fprintf(stderr, /* *very* unlikely to happen by accident */
"Non-standard uts for running kernel:\n"
"release %s=%d.%d.%d gives version code %d\n",
uts.release, x, y, z, LINUX_VERSION(x,y,z));
linux_version_code = LINUX_VERSION(x, y, z);
}

23
proc/version.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef PROC_VERSION_H
#define PROC_VERSION_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
*/
extern void display_version(void); /* display suite version */
extern char procps_version[]; /* global buf for suite version */
extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE
in /usr/include/linux/version.h */
/* Convenience macros for composing/decomposing version codes */
#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF)
#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF)
#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF)
#endif /* PROC_VERSION_H */

89
proc/whattime.c Normal file
View File

@ -0,0 +1,89 @@
/* This is a trivial uptime program. I hereby release this program
* into the public domain. I disclaim any responsibility for this
* program --- use it at your own risk. (as if there were any.. ;-)
* -michaelkjohnson (johnsonm@sunsite.unc.edu)
*
* Modified by Larry Greenfield to give a more traditional output,
* count users, etc. (greenfie@gauss.rutgers.edu)
*
* Modified by mkj again to fix a few tiny buglies.
*
* Modified by J. Cowley to add printing the uptime message to a
* string (for top) and to optimize file handling. 19 Mar 1993.
*
*/
#include <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 "proc/whattime.h"
#include "proc/sysinfo.h"
static char buf[128];
static double av[3];
char *sprint_uptime(void) {
struct utmp *utmpstruct;
int upminutes, uphours, updays;
int pos;
struct tm *realtime;
time_t realseconds;
int numuser;
double uptime_secs, idle_secs;
/* first get the current time */
time(&realseconds);
realtime = localtime(&realseconds);
pos = sprintf(buf, " %2d:%02d%s ",
realtime->tm_hour%12 ? realtime->tm_hour%12 : 12,
realtime->tm_min, realtime->tm_hour > 11 ? "pm" : "am");
/* read and calculate the amount of uptime */
uptime(&uptime_secs, &idle_secs);
updays = (int) uptime_secs / (60*60*24);
strcat (buf, "up ");
pos += 3;
if (updays)
pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : "");
upminutes = (int) uptime_secs / 60;
uphours = upminutes / 60;
uphours = uphours % 24;
upminutes = upminutes % 60;
if(uphours)
pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes);
else
pos += sprintf(buf + pos, "%d min, ", upminutes);
/* count the number of users */
numuser = 0;
setutent();
while ((utmpstruct = getutent())) {
if ((utmpstruct->ut_type == USER_PROCESS) &&
(utmpstruct->ut_name[0] != '\0'))
numuser++;
}
endutent();
pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s");
loadavg(&av[0], &av[1], &av[2]);
pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f",
av[0], av[1], av[2]);
return buf;
}
void print_uptime(void)
{
printf("%s\n", sprint_uptime());
}

9
proc/whattime.h Normal file
View File

@ -0,0 +1,9 @@
/* whattime.h --- see whattime.c for explanation */
#ifndef __WHATTIME_H
#define __WHATTIME_H
extern void print_uptime(void);
extern char *sprint_uptime(void);
#endif

16
procps.lsm Normal file
View File

@ -0,0 +1,16 @@
Begin3
Title: procps
Version: 010114
Entered-date: 14JAN01
Description: Procps is a library which parses the textual /proc filesystem
and a suite of utilites which use the library.
Keywords: procps /proc libproc
ps uptime tload free w top vmstat watch skill snice kill pgrep pkill
Author: Michael K. Johnson, Charles Blake, Albert Cahalan, many others.
Maintained-by: various <acahalan@cs.uml.edu>
Primary-site: http://www.cs.uml.edu/~acahalan/procps/
185kB procps-010114.tar.gz
Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html
185kB procps-010114.tar.gz
Copying-policy: mixed
End

105
procps.spec Normal file
View File

@ -0,0 +1,105 @@
Summary: Utilities for monitoring your system and processes on your system.
Name: procps
%define major_version 2
%define minor_version 0
%define revision 7
%define version %{major_version}.%{minor_version}.%{revision}
Version: %{version}
Release: 1
Copyright: GPL
Group: Applications/System
Source: ftp://people.redhat.com/johnsonm/procps/procps-%{version}.tar.gz
BuildRoot: /var/tmp/procps-root
%package X11
Group: Applications/System
Summary: X based process monitoring utilities.
%description
The procps package contains a set of system utilities which provide
system information. Procps includes ps, free, sysctl, skill, snice,
tload, top, uptime, vmstat, w, and watch. The ps command displays
a snapshot of running processes. The top command provides a
repetitive update of the statuses of running processes. The free
command displays the amounts of free and used memory on your system.
Sysctl is a simple program for managing system configuration entries.
The skill command sends a terminate command (or another
specified signal) to a specified set of processes. The snice
command is used to change the scheduling priority of specified
processes. The tload command prints a graph of the current system
load average to a specified tty. The uptime command displays the
current time, how long the system has been running, how many users
are logged on and system load averages for the past one, five and
fifteen minutes. The w command displays a list of the users who
are currently logged on and what they're running. The watch program
watches a running program. The vmstat command displays virtual
memory statistics about processes, memory, paging, block I/O, traps
and CPU activity.
%description X11
The procps-X11 package contains the XConsole shell script, a backwards
compatibility wrapper for the xconsole program.
%prep
%setup
%build
PATH=/usr/X11R6/bin:$PATH
make CC="gcc $RPM_OPT_FLAGS" LDFLAGS=-s
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/bin $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/sbin
mkdir -p $RPM_BUILD_ROOT/usr/man/man1 $RPM_BUILD_ROOT/usr/man/man8
mkdir -p $RPM_BUILD_ROOT/lib $RPM_BUILD_ROOT/usr/X11R6/bin
make DESTDIR=$RPM_BUILD_ROOT OWNERGROUP= MANDIR=/usr/share/man install
%clean
rm -rf $RPM_BUILD_ROOT
%post
# add libproc to the cache
/sbin/ldconfig
# remove obsolete files
rm -f /etc/psdevtab /etc/psdatabase
%files
%defattr(0644,root,root,755)
%attr(0644,root,root) %config(missingok) /etc/X11/applnk/Utilities/top.desktop
%doc NEWS BUGS TODO
%attr(755,root,root) /lib/libproc.so.2.0.7
%attr(555,root,root) /bin/ps
%attr(555,root,root) /sbin/sysctl
%attr(555,root,root) /usr/bin/oldps
%attr(555,root,root) /usr/bin/uptime
%attr(555,root,root) /usr/bin/tload
%attr(555,root,root) /usr/bin/free
%attr(555,root,root) /usr/bin/w
%attr(555,root,root) /usr/bin/top
%attr(555,root,root) /usr/bin/vmstat
%attr(555,root,root) /usr/bin/watch
%attr(555,root,root) /usr/bin/skill
%attr(555,root,root) /usr/bin/snice
%attr(555,root,root) /usr/bin/pgrep
%attr(555,root,root) /usr/bin/pkill
%attr(0644,root,root) /usr/man/man1/free.1
%attr(0644,root,root) /usr/man/man1/ps.1
%attr(0644,root,root) /usr/man/man1/oldps.1
%attr(0644,root,root) /usr/man/man1/sessreg.1
%attr(0644,root,root) /usr/man/man1/skill.1
%attr(0644,root,root) /usr/man/man1/snice.1
%attr(0644,root,root) /usr/man/man1/tload.1
%attr(0644,root,root) /usr/man/man1/top.1
%attr(0644,root,root) /usr/man/man1/uptime.1
%attr(0644,root,root) /usr/man/man1/w.1
%attr(0644,root,root) /usr/man/man1/watch.1
%attr(0644,root,root) /usr/man/man1/pgrep.1
%attr(0644,root,root) /usr/man/man1/pkill.1
%attr(0644,root,root) /usr/man/man8/vmstat.8
%attr(0644,root,root) /usr/man/man8/sysctl.8
%files X11
%attr(0755,root,root) /usr/X11R6/bin/XConsole

5
ps/.cvsignore.patch Normal file
View File

@ -0,0 +1,5 @@
diff -Naur procps-2.0.6/ps/.cvsignore procps-2.0.7/ps/.cvsignore
--- procps-2.0.6/ps/.cvsignore Wed Dec 31 19:00:00 1969
+++ procps-2.0.7/ps/.cvsignore Fri Jul 14 16:45:01 2000
@@ -0,0 +1 @@
+ps

481
ps/COPYING Normal file
View File

@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<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!

53
ps/HACKING Normal file
View File

@ -0,0 +1,53 @@
Warning:
This code must corrctly handle lots of picky little details to meet
the Unix98 standard while simultaneously being as compatible as
possible with the original Linux ps. Don't "fix" something without
considering the impact on all the special-case code. For example,
the "tty" format _must_ use "TT" as the header, even though the SysV
output formats _must_ use "TTY".
File overview:
display.c main(), debug code, iterates over processes
escape.c Does stuff like \202 and &lt; 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.

35
ps/Makefile Executable file
View File

@ -0,0 +1,35 @@
all: ps
ps: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o
$(CC) -o ps escape.o global.o help.o select.o sortformat.o output.o parser.o display.o -L../proc -lproc
# This just adds the stacktrace code
debug: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o
$(CC) -o ps escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o -L../proc -lproc -lefence
sortformat.o: sortformat.c common.h
global.o: global.c common.h
escape.o: escape.c
help.o: help.c
select.o: select.c common.h
output.o: output.c common.h
parser.o: parser.c common.h
display.o: display.c common.h
stacktrace.o: stacktrace.c
install: ps
install $(OWNERGROUP) --mode a=rx --strip ps $(BINDIR)/ps
install $(OWNERGROUP) --mode a=r ps.1 $(MAN1DIR)/ps.1
-rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz
clean:
rm -f *.o DEADJOE *~ core ps gmon.out

28
ps/TRANSLATION Normal file
View File

@ -0,0 +1,28 @@
Initially I only want to translate the --help output and man page.
Common error messages would be next on the list. I want to avoid
run-time overhead and bloat.
Translations of the --help output should not be longer than 22 lines long.
Feel free to leave out the less useful options to save space. (not even
the English help text has all the options)
I think these are the most important options:
*** selection ***
-C by command name list
-G by real group ID list (supports names)
-U by real user ID list (supports names)
-u by effective user ID list (supports names)
-e all processes
-p by process ID list
*** output ***
--no-heading No header line.
-o,o user-defined output
-j,j job control format
-l,l long format
-f full format
s signal format
u user-oriented format
--forest ASCII art forest (process heirarchy)
c show true command name

324
ps/common.h Normal file
View File

@ -0,0 +1,324 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#ifndef PROCPS_PS_H
#define PROCPS_PS_H
#include "../proc/readproc.h"
#include <asm/page.h> /* looks safe for glibc, we need PAGE_SIZE */
#if 0
#define trace(args...) printf(## args)
#else
#define trace(args...)
#endif
/***************** GENERAL DEFINE ********************/
/* selection list */
#define SEL_RUID 1
#define SEL_EUID 2
#define SEL_SUID 3
#define SEL_FUID 4
#define SEL_RGID 5
#define SEL_EGID 6
#define SEL_SGID 7
#define SEL_FGID 8
#define SEL_PGRP 9
#define SEL_PID 10
#define SEL_TTY 11
#define SEL_SESS 12
#define SEL_COMM 13
/* Since an enum could be smashed by a #define, it would be bad. */
#define U98 0 /* Unix98 standard */ /* This must be 0 */
#define XXX 1 /* Common extension */
#define DEC 2 /* Digital Unix */
#define AIX 3 /* AIX */
#define SCO 4 /* SCO */
#define LNX 5 /* Linux original :-) */
#define BSD 6 /* FreeBSD and OpenBSD */
#define SUN 7 /* SunOS 5 (Solaris) */
#define HPU 8 /* HP-UX */
#define SGI 9 /* Irix */
#define SOE 10 /* IBM's S/390 OpenEdition */
/*
* Must not overflow the output buffer:
* 32 pages for env+cmd
* 8 kB pages on the Alpha
* 5 chars for "\001 "
* plus some slack for other stuff
* That is about 1.3 MB on the Alpha
*
* This isn't good enough for setuid. If anyone cares, mmap() over the
* last page with something unwriteable.
*/
/* maximum escape expansion is 6, for &quot; */
#define ESC_STRETCH 6
/* output buffer size */
#define OUTBUF_SIZE (32*PAGE_SIZE*ESC_STRETCH + 8*PAGE_SIZE)
/* spaces used to right-justify things */
#define SPACE_AMOUNT (int)(PAGE_SIZE)
/******************* PS DEFINE *******************/
/* personality control flags */
#define PER_BROKEN_o 0x0001
#define PER_BSD_h 0x0002
#define PER_BSD_m 0x0004
#define PER_CUMUL_MARKED 0x0008
#define PER_FORCE_BSD 0x0010
#define PER_GOOD_o 0x0020
#define PER_OLD_m 0x0040
#define PER_NO_DEFAULT_g 0x0080
#define PER_ZAP_ADDR 0x0100
#define PER_SANE_USER 0x0200
#define PER_IRIX_l 0x0400
/* Simple selections by bit mask */
#define SS_B_x 0x01
#define SS_B_g 0x02
#define SS_U_d 0x04
#define SS_U_a 0x08
#define SS_B_a 0x10
/* predefined format flags such as: -l -f l u s -j */
#define FF_Uf 0x0001 /* -f */
#define FF_Uj 0x0002 /* -j */
#define FF_Ul 0x0004 /* -l */
#define FF_Bj 0x0008 /* j */
#define FF_Bl 0x0010 /* l */
#define FF_Bs 0x0020 /* s */
#define FF_Bu 0x0040 /* u */
#define FF_Bv 0x0080 /* v */
#define FF_LX 0x0100 /* X */
#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */
/* predefined format modifier flags such as: -l -f l u s -j */
#define FM_c 0x0001 /* -c */
#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */
#define FM_y 0x0004 /* -y */
#define FM_L 0x0008 /* -L */
#define FM_P 0x0010 /* -P */
#define FM_M 0x0020 /* -M */
#define FM_T 0x0040 /* -T */
#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */
/* sorting & formatting */
/* U,B,G is Unix,BSD,Gnu and then there is the option itself */
#define SF_U_O 1
#define SF_U_o 2
#define SF_B_O 3
#define SF_B_o 4
#define SF_B_m 5 /* overloaded: threads, sort, format */
#define SF_G_sort 6
#define SF_G_format 7
/* headers */
#define HEAD_SINGLE 0 /* default, must be 0 */
#define HEAD_NONE 1
#define HEAD_MULTI 2
/********************** GENERAL TYPEDEF *******************/
/* Other fields that might be useful:
*
* char *name; user-defined column name (format specification)
* int reverse; sorting in reverse (sort specification)
*
* name in place of u
* reverse in place of n
*/
typedef union sel_union {
pid_t pid;
uid_t uid;
gid_t gid;
dev_t tty;
char cmd[8]; /* this is _not_ \0 terminated */
} sel_union;
typedef struct selection_node {
struct selection_node *next;
sel_union *u; /* used if selection type has a list of values */
int n; /* used if selection type has a list of values */
int typecode;
} selection_node;
typedef struct sort_node {
struct sort_node *next;
int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */
int reverse; /* can sort backwards */
int typecode;
} sort_node;
typedef struct format_node {
struct format_node *next;
char *name; /* user can override default name */
int (*pr)(void); /* print function */
/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */
int width;
int pad;
int vendor; /* Vendor that invented this */
int flags;
int typecode;
} format_node;
typedef struct format_struct {
const char *spec; /* format specifier */
const char *head; /* default header in the POSIX locale */
int (* const pr)(void); /* print function */
int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */
const int width;
const int pad; /* could be second width */
const int vendor; /* Where does this come from? */
const int flags;
} format_struct;
/* though ps-specific, needed by general file */
typedef struct macro_struct {
const char *spec; /* format specifier */
const char *head; /* default header in the POSIX locale */
} macro_struct;
/**************** PS TYPEDEF ***********************/
typedef struct aix_struct {
const int desc; /* 1-character format code */
const char *spec; /* format specifier */
const char *head; /* default header in the POSIX locale */
} aix_struct;
typedef struct shortsort_struct {
const int desc; /* 1-character format code */
const char *spec; /* format specifier */
} shortsort_struct;
/* Save these options for later: -o o -O O --format --sort */
typedef struct sf_node {
struct sf_node *next; /* next arg */
format_node *f_cooked; /* convert each arg alone, then merge */
sort_node *s_cooked; /* convert each arg alone, then merge */
char *sf;
int sf_code;
} sf_node;
/*********************** GENERAL GLOBALS *************************/
/* escape.c */
extern int escape_strlist(char *dst, const char **src, size_t n);
extern int escape_str(char *dst, const char *src, size_t n);
extern int octal_escape_str(char *dst, const char *src, size_t n);
extern int simple_escape_str(char *dst, const char *src, size_t n);
/********************* UNDECIDED GLOBALS **************/
/*
* fputs(3) should (as in "good behavior") return the number of
* characters written as it does on Digital Unix, AIX, Irix, and SunOS.
* I'll assume glibc 2.1 has this extremely useful feature.
*
* Note: code ported from other systems will keep breaking until
* the library is updated. You should patch the library itself if
* at all possible. (for example, distributers who build libc from
* source with automatic patching as part of the build process)
*/
#if defined __GLIBC__ && ((__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) || __GLIBC__ > 2)
#warning Hopefully fputs(3) has been modernized...
#else
#define EMULATE_FPUTS
#define fputs something_to_avoid_libc_troubles
static inline int fputs(const char *s, FILE *fp){
return fwrite(s,1,strlen(s),fp);
/* return fprintf(fp, "%s", s); */
}
#endif
/* output.c */
extern void show_one_proc(proc_t* p);
extern void print_format_specifiers(void);
extern const aix_struct *search_aix_array(const int findme);
extern const shortsort_struct *search_shortsort_array(const int findme);
extern const format_struct *search_format_array(const char *findme);
extern const macro_struct *search_macro_array(const char *findme);
extern void init_output(void);
/* global.c */
extern void reset_global(void);
/* global.c */
extern int all_processes;
extern char *bsd_j_format;
extern char *bsd_l_format;
extern char *bsd_s_format;
extern char *bsd_u_format;
extern char *bsd_v_format;
extern int bsd_c_option;
extern int bsd_e_option;
extern uid_t cached_euid;
extern dev_t cached_tty;
extern char forest_prefix[4 * 32*1024 + 100];
extern int forest_type;
extern unsigned format_flags; /* -l -f l u s -j... */
extern format_node *format_list; /* digested formatting options */
extern unsigned format_modifiers; /* -c -j -y -P -L... */
extern int header_gap;
extern int header_type; /* none, single, multi... */
extern int include_dead_children;
extern int lines_to_next_header;
extern int max_line_width;
extern const char *namelist_file;
extern int negate_selection;
extern unsigned personality;
extern int prefer_bsd_defaults;
extern int running_only;
extern int screen_cols;
extern int screen_rows;
extern unsigned long seconds_since_boot;
extern selection_node *selection_list;
extern unsigned simple_select;
extern sort_node *sort_list;
extern char *sysv_f_format;
extern char *sysv_fl_format;
extern char *sysv_j_format;
extern char *sysv_l_format;
extern int unix_f_option;
extern int user_is_number;
extern int wchan_is_number;
/************************* PS GLOBALS *********************/
/* sortformat.c */
extern int defer_sf_option(const char *arg, int source);
extern const char *process_sf_options(int localbroken);
extern void reset_sortformat(void);
/* select.c */
extern int want_this_proc(proc_t *buf);
extern const char *select_bits_setup(void);
/* help.c */
extern const char *help_message;
/* global.c */
extern void self_info(void);
/* parser.c */
extern int arg_parse(int argc, char *argv[]);
#endif

398
ps/display.c Normal file
View File

@ -0,0 +1,398 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* 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/procps.h"
#include "../proc/version.h"
#include "../proc/readproc.h"
#include "../proc/sysinfo.h"
#ifndef SIGCHLD
#define SIGCHLD SIGCLD
#endif
/* just reports a crash */
static void signal_handler(int signo){
if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */
/* fprintf() is not reentrant, but we _exit() anyway */
fprintf(stderr,
"\n\n"
"Signal %d caught by ps (%s).\n"
"Please send bug reports to <acahalan@cs.uml.edu>\n",
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_pid(char *s, int n, sel_union *data){
printf("%s ", s);
while(--n){
printf("%d,", data[n].pid);
}
printf("%d\n", data[0].pid);
}
static void show_uid(char *s, int n, sel_union *data){
struct passwd *pw_data;
printf("%s ", s);
while(--n){
pw_data = getpwuid(data[n].uid);
if(pw_data) printf("%s,", pw_data->pw_name);
else printf("%d,", data[n].uid);
}
pw_data = getpwuid(data[n].uid);
if(pw_data) printf("%s\n", pw_data->pw_name);
else printf("%d\n", data[n].uid);
}
static void show_gid(char *s, int n, sel_union *data){
struct group *gr_data;
printf("%s ", s);
while(--n){
gr_data = getgrgid(data[n].gid);
if(gr_data) printf("%s,", gr_data->gr_name);
else printf("%d,", data[n].gid);
}
gr_data = getgrgid(data[n].gid);
if(gr_data) printf("%s\n", gr_data->gr_name);
else printf("%d\n", data[n].gid);
}
static void show_tty(char *s, int n, sel_union *data){
printf("%s ", s);
while(--n){
printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty));
}
printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty));
}
static void show_cmd(char *s, int n, sel_union *data){
printf("%s ", s);
while(--n){
printf("%.8s,", data[n].cmd);
}
printf("%.8s\n", data[0].cmd);
}
static void arg_show(void){
selection_node *walk = selection_list;
while(walk){
switch(walk->typecode){
case SEL_RUID: show_uid("RUID", walk->n, walk->u); break;
case SEL_EUID: show_uid("EUID", walk->n, walk->u); break;
case SEL_SUID: show_uid("SUID", walk->n, walk->u); break;
case SEL_FUID: show_uid("FUID", walk->n, walk->u); break;
case SEL_RGID: show_gid("RGID", walk->n, walk->u); break;
case SEL_EGID: show_gid("EGID", walk->n, walk->u); break;
case SEL_SGID: show_gid("SGID", walk->n, walk->u); break;
case SEL_FGID: show_gid("FGID", walk->n, walk->u); break;
case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break;
case SEL_PID : show_pid("PID ", walk->n, walk->u); break;
case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break;
case SEL_SESS: show_pid("SESS", walk->n, walk->u); break;
case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break;
default: printf("Garbage typecode value!\n");
}
walk = walk->next;
}
}
#endif
/***** check the header */
/* Unix98: must not print empty header */
static void check_headers(void){
format_node *walk = format_list;
int head_normal = 0;
if(header_type==HEAD_MULTI){
header_gap = screen_rows-1; /* true BSD */
return;
}
if(header_type==HEAD_NONE){
lines_to_next_header = -1; /* old Linux */
return;
}
while(walk){
if(!*(walk->name)){
walk = walk->next;
continue;
}
if(walk->pr){
head_normal++;
walk = walk->next;
continue;
}
walk = walk->next;
}
if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */
}
/***** fill in %CPU; not in libproc because of include_dead_children */
static void fill_pcpu(proc_t *buf){
unsigned long total_time;
unsigned long pcpu = 0;
unsigned long seconds;
total_time = buf->utime + buf->stime;
if(include_dead_children) total_time += (buf->cutime + buf->cstime);
seconds = (seconds_since_boot - ((unsigned long)buf->start_time) / Hertz);
if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
buf->pcpu = (pcpu > 999) ? 999 : pcpu;
}
/***** just display */
static void simple_spew(void){
proc_t buf;
PROCTAB* ptp;
ptp = openproc(PROC_FILLBUG);
if(!ptp) {
fprintf(stderr, "Error: can not access /proc.\n");
exit(1);
}
memset(&buf, '#', sizeof(proc_t));
/* use "ps_" prefix to catch library mismatch */
while(ps_readproc(ptp,&buf)){
fill_pcpu(&buf);
if(want_this_proc(&buf)) show_one_proc(&buf);
/* if(buf.cmdline) free(buf.cmdline); */ /* these crash */
/* if(buf.environ) free(buf.environ); */
memset(&buf, '#', sizeof(proc_t));
}
closeproc(ptp);
}
/***** forest output requires sorting by ppid; add start_time by default */
static void prep_forest_sort(void){
sort_node *tmp_list = sort_list;
const format_struct *incoming;
if(!sort_list) { /* assume start time order */
incoming = search_format_array("start_time");
if(!incoming) fprintf(stderr, "Could not find start_time!\n");
tmp_list = malloc(sizeof(sort_node));
tmp_list->reverse = 0;
tmp_list->typecode = '?'; /* what was this for? */
tmp_list->sr = incoming->sr;
tmp_list->next = sort_list;
sort_list = tmp_list;
}
/* this is required for the forest option */
incoming = search_format_array("ppid");
if(!incoming) fprintf(stderr, "Could not find ppid!\n");
tmp_list = malloc(sizeof(sort_node));
tmp_list->reverse = 0;
tmp_list->typecode = '?'; /* what was this for? */
tmp_list->sr = incoming->sr;
tmp_list->next = sort_list;
sort_list = tmp_list;
}
/* we rely on the POSIX requirement for zeroed memory */
static proc_t *processes[32*1024];
/***** compare function for qsort */
static int compare_two_procs(const void *a, const void *b){
sort_node *tmp_list = sort_list;
while(tmp_list){
int result;
result = (*tmp_list->sr)(*(const proc_t **)a, *(const proc_t **)b);
if(result) return (tmp_list->reverse) ? -result : result;
tmp_list = tmp_list->next;
}
return 0; /* no conclusion */
}
/***** show pre-sorted array of process pointers */
static void show_proc_array(int n){
proc_t **p = processes;
while(n--){
show_one_proc(*p);
/* if(p->cmdline) free(p->cmdline); */ /* this crashes */
/* if(p->environ) free(p->environ); */ /* this crashes */
/* memset(*p, '%', sizeof(proc_t)); */ /* debug */
free(*p);
p++;
}
}
/***** show tree */
/* this needs some optimization work */
#define ADOPTED(x) 1
static void show_tree(const int self, const int n, const int level, const int have_sibling){
int i = 0;
if(level){
/* add prefix of "+" or "L" */
if(have_sibling) forest_prefix[level-1] = '+';
else forest_prefix[level-1] = 'L';
forest_prefix[level] = '\0';
}
show_one_proc(processes[self]); /* first show self */
/* if(p->cmdline) free(p->cmdline); */ /* this crashes */
/* if(p->environ) free(p->environ); */ /* this crashes */
/* memset(*p, '%', sizeof(proc_t)); */ /* debug */
for(;;){ /* look for children */
if(i >= n) return; /* no children */
if(processes[i]->ppid == processes[self]->pid) break;
i++;
}
if(level){
/* change our prefix to "|" or " " for the children */
if(have_sibling) forest_prefix[level-1] = '|';
else forest_prefix[level-1] = ' ';
forest_prefix[level] = '\0';
}
for(;;){
int self_pid;
int more_children = 1;
if(i >= n) break; /* over the edge */
self_pid=processes[self]->pid;
if(i+1 >= n)
more_children = 0;
else
if(processes[i+1]->ppid != self_pid) more_children = 0;
if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u')
show_tree(i++, n, level, more_children);
else
show_tree(i++, n, level+1, more_children);
if(!more_children) break;
}
/* chop prefix that children added -- do we need this? */
forest_prefix[level] = '\0';
}
/***** show forest */
static void show_forest(const int n){
int i = n;
int j;
while(i--){ /* cover whole array looking for trees */
j = n;
while(j--){ /* search for parent: if none, i is a tree! */
if(processes[j]->pid == processes[i]->ppid) goto not_root;
}
show_tree(i,n,0,0);
not_root:
}
/* don't free the array because it takes time and ps will exit anyway */
}
/***** sorted or forest */
static void fancy_spew(void){
proc_t *retbuf;
PROCTAB* ptp;
int n = 0; /* number of processes & index into array */
ptp = openproc(PROC_FILLBUG);
if(!ptp) {
fprintf(stderr, "Error: can not access /proc.\n");
exit(1);
}
while((retbuf = ps_readproc(ptp,NULL))){
fill_pcpu(retbuf);
if(want_this_proc(retbuf)) processes[n++] = retbuf;
else free(retbuf);
}
closeproc(ptp);
if(!n) return; /* no processes */
if(forest_type) prep_forest_sort();
qsort(processes, n, sizeof(proc_t*), compare_two_procs);
if(forest_type) show_forest(n);
else show_proc_array(n);
}
/***** no comment */
int main(int argc, char *argv[]){
#ifdef DEBUG
init_stack_trace(argv[0]);
#else
do {
struct sigaction sa;
int i = 32;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigfillset(&sa.sa_mask);
while(i--) switch(i){
default:
sigaction(i,&sa,NULL);
case 0:
case SIGINT: /* ^C */
case SIGQUIT: /* ^\ */
case SIGPROF: /* profiling */
case SIGKILL: /* can not catch */
case SIGSTOP: /* can not catch */
case SIGWINCH: /* don't care if window size changes */
}
} while (0);
#endif
reset_global(); /* must be before parser */
arg_parse(argc,argv);
/* arg_show(); */
trace("screen is %ux%u\n",screen_cols,screen_rows);
/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */
trace("======= ps output follows =======\n");
init_output(); /* must be between parser and output */
check_headers();
if (open_psdb(namelist_file)) wchan_is_number = 1;
if(forest_type || sort_list) fancy_spew(); /* sort or forest */
else simple_spew(); /* no sort, no forest */
show_one_proc((proc_t *)-1); /* no output yet? */
return 0;
}

106
ps/escape.c Normal file
View File

@ -0,0 +1,106 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <sys/types.h>
/* sanitize a string, without the nice BSD library function: */
/* strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH) */
int octal_escape_str(char *dst, const char *src, size_t n){
unsigned char c;
char d;
size_t i;
const char *codes =
"Z------abtnvfr-------------e----"
" *******************************" /* better: do not print any space */
"****************************\\***"
"*******************************-"
"--------------------------------"
"********************************"
"********************************"
"********************************";
for(i=0; i<n;){
c = (unsigned char) *(src++);
d = codes[c];
switch(d){
case 'Z':
goto leave;
case '*':
i++;
*(dst++) = c;
break;
case '-':
if(i+4 > n) goto leave;
i += 4;
*(dst++) = '\\';
*(dst++) = "01234567"[c>>6];
*(dst++) = "01234567"[(c>>3)&07];
*(dst++) = "01234567"[c&07];
break;
default:
if(i+2 > n) goto leave;
i += 2;
*(dst++) = '\\';
*(dst++) = d;
break;
}
}
leave:
*(dst++) = '\0';
return i;
}
/* sanitize a string via one-way mangle */
int simple_escape_str(char *dst, const char *src, size_t n){
unsigned char c;
size_t i;
const char *codes =
"Z-------------------------------"
"********************************"
"********************************"
"*******************************-"
"--------------------------------"
"********************************"
"********************************"
"********************************";
for(i=0; i<n;){
c = (unsigned char) *(src++);
switch(codes[c]){
case 'Z':
goto leave;
case '*':
i++;
*(dst++) = c;
break;
case '-':
i++;
*(dst++) = '?';
break;
}
}
leave:
*(dst++) = '\0';
return i;
}
/* escape a string as desired */
int escape_str(char *dst, const char *src, size_t n){
return simple_escape_str(dst, src, n);
}
/* escape an argv or environment string array */
int escape_strlist(char *dst, const char **src, size_t n){
size_t i = 0;
while(*src){
i += simple_escape_str(dst+i, *src, n-i);
if((n-i > 1) && (*(src+1))) dst[i++] = ' ';
src++;
}
return i;
}

422
ps/global.c Normal file
View File

@ -0,0 +1,422 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
/*#undef __GLIBC_MINOR__
#define __GLIBC_MINOR__ 1 */
#include "common.h"
/*#undef __GLIBC_MINOR__
#define __GLIBC_MINOR__ 0 */
#include <sys/sysmacros.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 char *saved_personality_text = "You found a bug!";
int all_processes = -1;
char *bsd_j_format = (char *)0xdeadbeef;
char *bsd_l_format = (char *)0xdeadbeef;
char *bsd_s_format = (char *)0xdeadbeef;
char *bsd_u_format = (char *)0xdeadbeef;
char *bsd_v_format = (char *)0xdeadbeef;
int bsd_c_option = -1;
int bsd_e_option = -1;
uid_t cached_euid = -1;
dev_t cached_tty = -1;
char forest_prefix[4 * 32*1024 + 100];
int forest_type = -1;
unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */
format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */
unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */
int header_gap = -1;
int header_type = -1;
int include_dead_children = -1;
int lines_to_next_header = -1;
const char *namelist_file = (const char *)0xdeadbeef;
int negate_selection = -1;
int running_only = -1;
unsigned personality = 0xffffffff;
int prefer_bsd_defaults = -1;
int screen_cols = -1;
int screen_rows = -1;
unsigned long seconds_since_boot = -1;
selection_node *selection_list = (selection_node *)0xdeadbeef;
unsigned simple_select = 0xffffffff;
sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
char *sysv_f_format = (char *)0xdeadbeef;
char *sysv_fl_format = (char *)0xdeadbeef;
char *sysv_j_format = (char *)0xdeadbeef;
char *sysv_l_format = (char *)0xdeadbeef;
int unix_f_option = -1;
int user_is_number = -1;
int wchan_is_number = -1;
static void reset_selection_list(void){
selection_node *old;
selection_node *walk = selection_list;
if(selection_list == (selection_node *)0xdeadbeef){
selection_list = NULL;
return;
}
while(walk){
old = walk;
walk = old->next;
free(old->u);
free(old);
}
selection_list = NULL;
}
/* The rules:
* 1. Defaults are implementation-specific. (ioctl,termcap,guess)
* 2. COLUMNS and LINES override the defaults. (standards compliance)
* 3. Command line options override everything else.
* 4. Actual output may be more if the above is too narrow.
*/
static void set_screen_size(void){
struct winsize ws;
char *columns; /* Unix98 environment variable */
char *lines; /* Unix98 environment variable */
if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0){
screen_cols = ws.ws_col;
screen_rows = ws.ws_row;
}else{ /* TODO: ought to do tgetnum("co") and tgetnum("li") now */
screen_cols = 80;
screen_rows = 24;
}
if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE;
columns = getenv("COLUMNS");
if(columns && *columns){
long t;
char *endptr;
t = strtol(columns, &endptr, 0);
if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t;
}
lines = getenv("LINES");
if(lines && *lines){
long t;
char *endptr;
t = strtol(lines, &endptr, 0);
if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t;
}
if((screen_cols<9) || (screen_rows<2))
fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n",
screen_cols, screen_rows
);
}
/**************** personality control **************/
typedef struct personality_table_struct {
const char *name; /* personality name */
const void *jump; /* See gcc extension info. :-) */
} personality_table_struct;
static int compare_personality_table_structs(const void *a, const void *b){
return strcasecmp(((personality_table_struct*)a)->name,((personality_table_struct*)b)->name);
}
static const char *set_personality(void){
char *s;
size_t sl;
char buf[16];
personality_table_struct findme = { buf, NULL};
personality_table_struct *found;
static const personality_table_struct personality_table[] = {
{"390", &&case_390},
{"aix", &&case_aix},
{"bsd", &&case_bsd},
{"compaq", &&case_compaq},
{"debian", &&case_debian},
{"default", &&case_default},
{"digital", &&case_digital},
{"gnu", &&case_gnu},
{"hp", &&case_hp},
{"hpux", &&case_hpux},
{"irix", &&case_irix},
{"linux", &&case_linux},
{"old", &&case_old},
{"os390", &&case_os390},
{"posix", &&case_posix},
{"s390", &&case_s390},
{"sco", &&case_sco},
{"sgi", &&case_sgi},
{"solaris2", &&case_solaris2},
{"sunos4", &&case_sunos4},
{"sysv", &&case_sysv},
{"tru64", &&case_tru64},
{"unix", &&case_unix},
{"unix95", &&case_unix95},
{"unix98", &&case_unix98},
{"unknown", &&case_unknown}
};
const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct);
personality = 0;
prefer_bsd_defaults = 0;
bsd_j_format = "OL_j";
bsd_l_format = "OL_l";
bsd_s_format = "OL_s";
bsd_u_format = "OL_u";
bsd_v_format = "OL_v";
/* When these are NULL, the code does SysV output modifier logic */
sysv_f_format = NULL;
sysv_fl_format = NULL;
sysv_j_format = NULL;
sysv_l_format = NULL;
s = getenv("PS_PERSONALITY");
if(!s || !*s) s = getenv("CMD_ENV");
if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */
if(getenv("I_WANT_A_BROKEN_PS")) s="old";
sl = strlen(s);
if(sl > 15) return "Environment specified an unknown personality.";
strncpy(buf, s, sl);
buf[sl] = '\0';
saved_personality_text = strdup(buf);
found = bsearch(&findme, personality_table, personality_table_count,
sizeof(personality_table_struct), compare_personality_table_structs
);
if(!found) return "Environment specified an unknown personality.";
goto *(found->jump); /* See gcc extension info. :-) */
case_bsd:
personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m;
prefer_bsd_defaults = 1;
bsd_j_format = "FB_j";
bsd_l_format = "FB_l";
/* bsd_s_format not used */
bsd_u_format = "FB_u";
bsd_v_format = "FB_v";
return NULL;
case_old:
personality = PER_FORCE_BSD | PER_OLD_m;
prefer_bsd_defaults = 1;
return NULL;
case_debian: /* Toss this? They don't seem to care much. */
case_gnu:
personality = PER_GOOD_o | PER_CUMUL_MARKED | PER_OLD_m;
prefer_bsd_defaults = 1;
sysv_f_format = "RD_f";
/* sysv_fl_format = "RD_fl"; */ /* Debian can't do this! */
sysv_j_format = "RD_j";
sysv_l_format = "RD_l";
return NULL;
case_linux:
personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER;
return NULL;
case_default: /* use defaults for ps, ignoring other environment variables */
return NULL;
case_unknown: /* defaults, but also check inferior environment variables */
if(
getenv("UNIX95") /* Irix */
|| getenv("POSIXLY_CORRECT") /* most gnu stuff */
|| (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */
) personality = PER_BROKEN_o;
return NULL;
case_aix:
bsd_j_format = "FB_j";
bsd_l_format = "FB_l";
/* bsd_s_format not used */
bsd_u_format = "FB_u";
bsd_v_format = "FB_v";
return NULL;
case_tru64:
case_compaq:
case_digital:
personality = PER_GOOD_o | PER_BSD_h;
prefer_bsd_defaults = 1;
sysv_f_format = "F5FMT";
sysv_fl_format = "FL5FMT";
sysv_j_format = "JFMT";
sysv_l_format = "L5FMT";
bsd_j_format = "JFMT";
bsd_l_format = "LFMT";
bsd_s_format = "SFMT";
bsd_u_format = "UFMT";
bsd_v_format = "VFMT";
return NULL;
case_sunos4:
personality = PER_NO_DEFAULT_g;
prefer_bsd_defaults = 1;
bsd_j_format = "FB_j";
bsd_l_format = "FB_l";
/* bsd_s_format not used */
bsd_u_format = "FB_u";
bsd_v_format = "FB_v";
return NULL;
case_irix:
case_sgi:
s = getenv("_XPG");
if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o;
else personality = PER_IRIX_l;
return NULL;
case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */
case_s390:
case_390:
sysv_j_format = "J390"; /* don't know what -jl and -jf do */
return NULL;
case_hp:
case_hpux:
case_posix:
case_sco:
case_solaris2:
case_sysv:
case_unix95:
case_unix98:
case_unix:
personality = PER_BROKEN_o;
return NULL;
}
/************ Call this to reinitialize everything ***************/
void reset_global(void){
static proc_t p;
reset_selection_list();
look_up_our_self(&p);
set_screen_size();
set_personality();
all_processes = 0;
bsd_c_option = 0;
bsd_e_option = 0;
cached_euid = geteuid();
cached_tty = p.tty;
/* forest_prefix must be all zero because of POSIX */
forest_type = 0;
format_flags = 0; /* -l -f l u s -j... */
format_list = NULL; /* digested formatting options */
format_modifiers = 0; /* -c -j -y -P -L... */
header_gap = -1; /* send lines_to_next_header to -infinity */
header_type = HEAD_SINGLE;
include_dead_children = 0;
lines_to_next_header = 1;
namelist_file = NULL;
negate_selection = 0;
running_only = 0;
seconds_since_boot = uptime(0,0);
selection_list = NULL;
simple_select = 0;
sort_list = NULL;
unix_f_option = 0;
user_is_number = 0;
wchan_is_number = 0;
}
/*********** spew variables ***********/
void self_info(void){
#ifndef EMULATE_FPUTS
int count;
#endif
fprintf(stderr,
"BSD j %s\n"
"BSD l %s\n"
"BSD s %s\n"
"BSD u %s\n"
"BSD v %s\n"
"SysV -f %s\n"
"SysV -fl %s\n"
"SysV -j %s\n"
"SysV -l %s\n"
"\n",
bsd_j_format ? bsd_j_format : "(none)",
bsd_l_format ? bsd_l_format : "(none)",
bsd_s_format ? bsd_s_format : "(none)",
bsd_u_format ? bsd_u_format : "(none)",
bsd_v_format ? bsd_v_format : "(none)",
sysv_f_format ? sysv_f_format : "(none)",
sysv_fl_format ? sysv_fl_format : "(none)",
sysv_j_format ? sysv_j_format : "(none)",
sysv_l_format ? sysv_l_format : "(none)"
);
display_version();
fprintf(stderr, "Linux version %d.%d.%d\n",
LINUX_VERSION_MAJOR(linux_version_code),
LINUX_VERSION_MINOR(linux_version_code),
LINUX_VERSION_PATCH(linux_version_code)
);
/* __libc_print_version(); */ /* how can we get the run-time version? */
fprintf(stderr, "Compiled with: libc %d, internal version %d.%d\n\n",
__GNU_LIBRARY__, __GLIBC__, __GLIBC_MINOR__
);
#ifdef EMULATE_FPUTS
fprintf(stderr, "libc assumed lame, using fprintf to emulate fputs.\n\n");
#else
fprintf(stderr, "fputs(\"");
count = fputs("123456789", stderr);
fprintf(stderr, "\", stderr) gives %d, which is %s.\n",
count, count==9?"good":"BAD!\nAdjust ps/common.h or libc, then recompile"
);
if(count!=9){
fprintf(stderr, "(procps includes a libc patch called glibc.patch)\n");
}
fprintf(stderr, "\n");
#endif
fprintf(stderr,
"header_gap=%d lines_to_next_header=%d\n"
"screen_cols=%d screen_rows=%d\n"
"\n",
header_gap, lines_to_next_header,
screen_cols, screen_rows
);
/* open_psdb(namelist_file); */
fprintf(stderr,
"personality=0x%08x (from \"%s\")\n"
"EUID=%d TTY=%d,%d Hertz=%ld\n"
/* "namelist_file=\"%s\"\n" */
,
personality, saved_personality_text,
cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz /* ,
namelist_file?namelist_file:"<no System.map file>" */
);
}

49
ps/help.c Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright 1998 by Albert Cahalan; all rights reserved.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
/*
* The help message must not become longer, because it must fit
* on an 80x24 screen _with_ the error message and command prompt.
*/
const char *help_message =
"********* simple selection ********* ********* selection by list *********\n"
"-A all processes -C by command name\n"
"-N negate selection -G by real group ID (supports names)\n"
"-a all w/ tty except session leaders -U by real user ID (supports names)\n"
"-d all except session leaders -g by session leader OR by group name\n"
"-e all processes -p by process ID\n"
"T all processes on this terminal -s processes in the sessions given\n"
"a all w/ tty, including other users -t by tty\n"
"g all, even group leaders! -u by effective user ID (supports names)\n"
"r only running processes U processes for specified users\n"
"x processes w/o controlling ttys t by tty\n"
"*********** output format ********** *********** long options ***********\n"
"-o,o user-defined -f full --Group --User --pid --cols\n"
"-j,j job control s signal --group --user --sid --rows\n"
"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n"
"-l,l long u user-oriented --sort --tty --forest --version\n"
" X registers --heading --no-heading\n"
" ********* misc options *********\n"
"-V,V show version L list format codes f ASCII art forest\n"
"-m,m show threads S children in sum -y change -l format\n"
"-n,N set namelist file c true command name n numeric WCHAN,UID\n"
"-w,w wide output e show environment -H process heirarchy\n"
;
/* Missing:
*
* -c -L -P -M --info
*
*/

35
ps/it Normal file
View File

@ -0,0 +1,35 @@
From ddainese@dsi.unive.it Sun Apr 18 14:12:27 1999
here is a first translation of the text:
---------------------------------------------------------------------
const char *help_message =
"****** seleziona i processi ******* * seleziona una lista specificando: *\n"
"-A tutti -C il nome del comando\n"
"-N nega la selezione -G il real group ID (supporta i nomi)\n"
"-a con tty, tranne i session leader -U il real user ID (supporta i nomi)\n"
"-d tutti, tranne i session leader -g il session leader OPPURE il gruppo\n"
"-e tutti -p l'ID del processo\n"
"T su questo terminale -s la sessione\n"
"a con tty, di tutti gli utenti -t il tty\n"
"g tutti, anche i leader di gruppo -u l'effective user ID (supporta i nomi)\n"
"r in stato running U una lista di utenti\n"
"x senza tty t il tty\n"
"******** formato dell'output ****** ********** opzioni lunghe **********\n"
"-o,o definito dall'utente --Group --User --pid --cols\n"
"-j,j job s segnali --group --user --sid --rows\n"
"-O,O -o preimpostato v memoria virtuale --cumulative --format --deselect\n"
"-l,l lungo u utenti --sort --tty --forest --version\n"
"-f completo X registri --heading --no-heading\n"
" ******** opzioni varie *********\n"
"-V,V versione L codici di formato f foresta di ASCII\n"
"-m,m vista ad albero S figli in sum -y cambia il formato -l\n"
"-n,N namelist file c nome reale del comando n WCHAN,UID numerici\n"
"-w,w output ampio e mostra l'environment -H gerarchia dei processi\n"
;
---------------------------------------------------------------------
Unfortunately it isn't really understandable for a newbie, because
there is too little space for a good translation; to make it more
meaningful, I would need about an entire line for every option, thus
if you really want the help text stays under 22 lines, it must
contains only 22 options. What do you think about it?

1608
ps/output.c Normal file

File diff suppressed because it is too large Load Diff

6
ps/p Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
#
# Wow, using $* causes great pain with: ps "pid,user pcpu,pmem"
# The "$@" won't break that into 2 arguments.
#
LD_PRELOAD=../proc/libproc.so exec ./ps "$@"

1123
ps/parser.c Normal file

File diff suppressed because it is too large Load Diff

521
ps/ps.1 Normal file
View File

@ -0,0 +1,521 @@
.\" Man page for ps.
.\" Quick hack conversion by Albert Cahalan, 1998.
.\" Licensed under version 2 of the Gnu General Public License.
.\"
.\" This man page is a horrid hack because *roff sucks.
.\" The whole system is way obsolete. The internal header
.\" stuff must die, and will when I figure out how to kill it.
.\" I've already killed the wasteful left margin and screwy
.\" old perfect justification. Gross! You'd think someone
.\" invented this crap in 1973. Oh yeah, they did. Sorry.
.\"
.TH PS 1 "July 5, 1998" "Linux" "Linux User's Manual"
.SH \fRNAME\fR
ps \- report process status
.ad r
.na
.ss 12 0
.in 0
.nh
.nf
SYNOPSIS
ps [options]
DESCRIPTION
ps gives a snapshot of the current processes. If you want
a repetitive update of this status, use top. This man
page documents the /proc-based version of ps, or tries to.
COMMAND-LINE OPTIONS
This version of ps accepts several kinds of options.
Unix options may be grouped and must be preceeded by a dash.
BSD options may be grouped and must not be used with a dash.
Gnu long options are preceeded by two dashes.
Options of different types may be freely mixed.
Set the I_WANT_A_BROKEN_PS environment variable to force BSD syntax even
when options are preceeded by a dash. The PS_PERSONALITY environment
variable (described below) provides more detailed control of ps behavior.
SIMPLE PROCESS SELECTION
-A select all processes
-N negate selection
-a select all with a tty except session leaders
-d select all, but omit session leaders
-e select all processes
T select all processes on this terminal
a select all processes on a terminal, including those of other users
g really all, even group leaders (does nothing w/o SunOS settings)
r restrict output to running processes
x select processes without controlling ttys
--deselect negate selection
PROCESS SELECTION BY LIST
-C select by command name
-G select by RGID (supports names)
-U select by RUID (supports names)
-g select by session leader OR by group name
-p select by PID
-s select processes belonging to the sessions given
-t select by tty
-u select by effective user ID (supports names)
U select processes for specified users
p select by process ID
t select by tty
--Group select by real group name or ID
--User select by real user name or ID
--group select by effective group name or ID
--pid select by process ID
--sid select by session ID
--tty select by terminal
--user select by effective user name or ID
-123 implied --sid
123 implied --pid
OUTPUT FORMAT CONTROL
-O is preloaded "-o"
-c different scheduler info for -l option
-f does full listing
-j jobs format
-l long format
-o user-defined format
-y do not show flags; show rss in place of addr
O is preloaded "o" (overloaded)
X old Linux i386 register format
j job control format
l display long format
o specify user-defined format
s display signal format
u display user-oriented format
v display virtual memory format
--format user-defined format
OUTPUT MODIFIERS
-H show process hierarchy (forest)
-m show threads
-n set namelist file
-w wide output
C use raw CPU time for %CPU instead of decaying average
N specify namelist file
O sorting order (overloaded)
S include some dead child process data (as a sum with the parent)
c true command name
e show environment after the command
f ASCII-art process hierarchy (forest)
h no header (or, one header per screen in the BSD personality)
m all threads
n numeric output for WCHAN and USER
w wide output
--cols set screen width
--columns set screen width
--cumulative include some dead child process data (as a sum with the parent)
--forest ASCII art process tree
--headers repeat header lines, one per page of output
--no-headers print no header line at all
--lines set screen height
--rows set screen height
--sort specify sorting order
--width set screen width
INFORMATION
-V print version
L list all format specifiers
V show version info
--help print help message
--info print debugging info
--version print version
OBSOLETE
A increases the argument space (DecUnix)
M use alternate core (try -n or N instead)
W get swap info from ... not /dev/drum (try -n or N instead)
k use /vmcore as c-dumpfile (try -n or N instead)
NOTES
The "-g" option can select by session leader OR by group name.
Selection by session leader is specified by many standards,
but selection by group is the logical behavior that several other
operating systems use. This ps will select by session leader when
the list is completely numeric (as sessions are). Group ID numbers
will work only when some group names are also specified.
The "m" option should not be used. Use "-m" or "-o" with a list.
("m" displays memory info, shows threads, or sorts by memory use)
The "h" option is problematic. Standard BSD ps uses the option to
print a header on each page of output, but older Linux ps uses the option
to totally disable the header. This version of ps 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
--headers and --no-headers to enable printing headers each page and
disable headers entirely, respectively.
Terminals (ttys, or screens for text output) can be specified in several
forms: /dev/ttyS1, ttyS1, S1. Obsolete "ps t" (your own terminal) and
"ps t?" (processes without a terminal) syntax is supported, but modern
options ("T", "-t" with list, "x", "t" with list) should be used instead.
The BSD "O" option can act like "-O" (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, specify the other option (sorting
or formatting) in some other way.
For sorting, BSD "O" option syntax is O[+|-]k1[,[+|-]k2[,...]]
Order the process listing according to the multilevel sort specified by
the sequence of short keys from SORT KEYS, k1, k2, ... The `+' is quite
optional, merely re-iterating the default direction on a key. `-' reverses
direction only on the key it precedes. The O option must be the last option
in a single command argument, but specifications in successive arguments are
catenated.
Gnu sorting syntax is --sortX[+|-]key[,[+|-]key[,...]]
Choose a multi-letter key from the SORT KEYS section. X may be any
convenient separator character. To be GNU-ish use `='. The `+' is really
optional since default direction is increasing numerical or lexicographic
order. For example, ps jax --sort=uid,-ppid,+pid
This ps works by reading the virtual files in /proc. This ps does not
need to be suid kmem or have any privileges to run. Do not give this ps
any special permissions.
This ps needs access to a namelist file for proper WCHAN display.
The namelist file must match the current Linux kernel exactly for
correct output.
To produce the WCHAN field, ps needs to read the System.map file created
when the kernel is compiled. The search path is:
$PS_SYSTEM_MAP
/boot/System.map-`uname -r`
/boot/System.map
/lib/modules/`uname -r`/System.map
/usr/src/linux/System.map
The member used_math of task_struct is not shown, since crt0.s checks
to see if math is present. This causes the math flag to be set for all
processes, and so it is worthless. (Somebody fix libc or the kernel please)
Programs swapped out to disk will be shown without command line arguments,
and unless the c option is given, in brackets.
%CPU shows the cputime/realtime percentage. It will not add up to 100%
unless you are lucky. It is time used divided by the time the process has
been running.
The SIZE and RSS fields don't count the page tables and the task_struct of a
proc; this is at least 12k of memory that is always resident. SIZE is the
virtual size of the proc (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 init(8) if the parent process exits.
PROCESS FLAGS
ALIGNWARN 001 print alignment warning msgs
STARTING 002 being created
EXITING 004 getting shut down
PTRACED 010 set if ptrace (0) has been called
TRACESYS 020 tracing system calls
FORKNOEXEC 040 forked but didn't exec
SUPERPRIV 100 used super-user privileges
DUMPCORE 200 dumped core
SIGNALED 400 killed by a signal
PROCESS STATE CODES
D uninterruptible sleep (usually IO)
R runnable (on run queue)
S sleeping
T traced or stopped
Z a defunct ("zombie") process
For BSD formats and when the "stat" keyword is used, additional
letters may be displayed:
W has no resident pages
< high-priority process
N low-priority task
L has pages locked into memory (for real-time and custom IO)
SORT KEYS
Note that the values used in sorting are the internal values ps uses and not
the `cooked' values used in some of the output format fields. Pipe ps
output into the sort(1) command if you want to sort the cooked values.
KEY LONG DESCRIPTION
c cmd simple name of executable
C cmdline full command line
f flags flags as in long format F field
g pgrp process group ID
G tpgid controlling tty process group ID
j cutime cumulative user time
J cstime cumulative system time
k utime user time
K stime system time
m min_flt number of minor page faults
M maj_flt number of major page faults
n cmin_flt cumulative minor page faults
N cmaj_flt cumulative major page faults
o session session ID
p pid process ID
P ppid parent process ID
r rss resident set size
R resident resident pages
s size memory size in kilobytes
S share amount of shared pages
t tty the minor device number of tty
T start_time time process was started
U uid user ID number
u user user name
v vsize total VM size in kB
y priority kernel scheduling priority
AIX FORMAT DESCRIPTORS
This ps supports AIX format descriptors, which work somewhat like the
formatting codes of printf(1) and printf(3). For example, the normal
default output can be produced with this: ps -eo "%p %y %x %c"
CODE NORMAL HEADER
%C pcpu %CPU
%G group GROUP
%P ppid PPID
%U user USER
%a args COMMAND
%c comm COMMAND
%g rgroup RGROUP
%n nice NI
%p pid PID
%r pgid PGID
%t etime ELAPSED
%u ruser RUSER
%x time TIME
%y tty TTY
%z vsz VSZ
STANDARD FORMAT SPECIFIERS
These may be used to control both output format and sorting.
For example: ps -eo pid,user,args --sort user
CODE HEADER
%cpu %CPU
%mem %MEM
alarm ALARM
args COMMAND
blocked BLOCKED
bsdstart START
bsdtime TIME
c C
caught CAUGHT
cmd CMD
comm COMMAND
command COMMAND
cputime TIME
drs DRS
dsiz DSIZ
egid EGID
egroup EGROUP
eip EIP
esp ESP
etime ELAPSED
euid EUID
euser EUSER
f F
fgid FGID
fgroup FGROUP
flag F
flags F
fname COMMAND
fsgid FSGID
fsgroup FSGROUP
fsuid FSUID
fsuser FSUSER
fuid FUID
fuser FUSER
gid GID
group GROUP
ignored IGNORED
intpri PRI
lim LIM
longtname TTY
lstart STARTED
m_drs DRS
m_trs TRS
maj_flt MAJFL
majflt MAJFLT
min_flt MINFL
minflt MINFLT
ni NI
nice NI
nwchan WCHAN
opri PRI
pagein PAGEIN
pcpu %CPU
pending PENDING
pgid PGID
pgrp PGRP
pid PID
pmem %MEM
ppid PPID
pri PRI
priority PRI
rgid RGID
rgroup RGROUP
rss RSS
rssize RSS
rsz RSZ
ruid RUID
ruser RUSER
s S
sess SESS
session SESS
sgi_p P
sgi_rss RSS
sgid SGID
sgroup SGROUP
sid SID
sig PENDING
sig_block BLOCKED
sig_catch CATCHED
sig_ignore IGNORED
sig_pend SIGNAL
sigcatch CAUGHT
sigignore IGNORED
sigmask BLOCKED
stackp STACKP
start STARTED
start_stack STACKP
start_time START
stat STAT
state S
stime STIME
suid SUID
suser SUSER
svgid SVGID
svgroup SVGROUP
svuid SVUID
svuser SVUSER
sz SZ
time TIME
timeout TMOUT
tmout TMOUT
tname TTY
tpgid TPGID
trs TRS
trss TRSS
tsiz TSIZ
tt TT
tty TT
tty4 TTY
tty8 TTY
ucmd CMD
ucomm COMMAND
uid UID
uid_hack UID
uname USER
user USER
vsize VSZ
vsz VSZ
wchan WCHAN
ENVIRONMENT VARIABLES
The following environment variables could affect ps:
COLUMNS Override default display width.
LINES Override default display height.
PS_PERSONALITY Set to one of posix,old,linux,bsd,sun,digital...
CMD_ENV Set to one of posix,old,linux,bsd,sun,digital...
I_WANT_A_BROKEN_PS Force obsolete command line interpretation.
LC_TIME Date format.
PS_COLORS Not currently supported.
PS_FORMAT Default output format override.
PS_SYSMAP Default namelist (System.map) location.
PS_SYSTEM_MAP Default namelist (System.map) location.
POSIXLY_CORRECT Don't find excuses to ignore bad "features".
UNIX95 Don't find excuses to ignore bad "features".
_XPG Cancel CMD_ENV=irix non-standard behavior.
In general, it is a bad idea to set these variables. The one exception
is CMD_ENV or PS_PERSONALITY, which could be set to Linux for normal
systems. Without that setting, ps follows the useless and bad parts
of the Unix98 standard.
PERSONALITY
390 like the S/390 OpenEdition ps
aix like AIX ps
bsd like FreeBSD ps (totally non-standard)
compaq like Digital Unix ps
debian like the old Debian ps
digital like Digital Unix ps
gnu like the old Debian ps
hp like HP-UX ps
hpux like HP-UX ps
irix like Irix ps
linux ***** RECOMMENDED *****
old like the original Linux ps (totally non-standard)
posix standard
sco like SCO ps
sgi like Irix ps
sun like SunOS 4 ps (totally non-standard)
sunos like SunOS 4 ps (totally non-standard)
sysv standard
unix standard
unix95 standard
unix98 standard
EXAMPLES
To see every process on the system using standard syntax:
ps -e
To see every process on the system using BSD syntax:
ps ax
To see every process except those running as root (real & effective ID)
ps -U root -u root -N
To see every process with a user-defined format:
ps -eo pid,tt,user,fname,tmout,f,wchan
Odd display with AIX field descriptors:
ps -o "%u : %U : %p : %a"
Print only the process IDs of syslogd:
ps -C syslogd -o pid=
SEE ALSO
top(1) pstree(1) proc(5)
STANDARDS
This ps conforms to version 2 of the Single Unix Specification.
AUTHOR
ps was originally written by Branko Lankester <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
<acahalan@cs.uml.edu> rewrote ps for full Unix98 and BSD support, along with
some ugly hacks for obsolete and foreign syntax.
Please send bug reports to <acahalan@cs.uml.edu>

26
ps/regression Normal file
View File

@ -0,0 +1,26 @@
-u 500 -o pid,ppid,fname,comm,args # right margin trouble
-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan
-u 500 -o pid,pid,pid,pid,user,user,user,args # had trouble
-u 500 -o user,user,user,pid,pid,pid,pid,args # no trouble!
Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the
edge of the screen and each type of field to the left of the one that
hangs off the edge.
Test "ps ef" as _both_ normal user and root. Especially after su!
On a 108-col screen, try "ps alx" and "ps alx | cat"
These ought to be the same:
CMD_ENV=old ps -m
CMD_ENV=old ps m
These ought to be the same:
CMD_ENV=old ps -X
CMD_ENV=old ps X
ps X
ps -X # needs to be a non-SysV option
This should fail:
ps x -x

146
ps/select.c Normal file
View File

@ -0,0 +1,146 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "common.h"
#include "../proc/readproc.h"
#include "../proc/procps.h"
#define session_leader(p) ((p)->session == (p)->pid)
#define process_group_leader(p) ((p)->pgid == (p)->pid)
#define without_a_tty(p) ((unsigned short)((p)->tty) == (unsigned short)-1)
#define some_other_user(p) ((p)->euid != cached_euid)
#define running(p) (((p)->state=='R')||((p)->state=='D'))
#define has_our_euid(p) ((unsigned short)((p)->euid) == (unsigned short)cached_euid)
#define on_our_tty(p) ((unsigned short)((p)->tty) == (unsigned short)cached_tty)
static unsigned long select_bits = 0;
/***** prepare select_bits for use */
const char *select_bits_setup(void){
int switch_val = 0;
/* don't want a 'g' screwing up simple_select */
if(!simple_select && !prefer_bsd_defaults){
select_bits = 0xaa00; /* the STANDARD selection */
return NULL;
}
/* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */
if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) )
switch_val = simple_select|SS_B_g;
else
switch_val = simple_select;
switch(switch_val){
/* UNIX options */
case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */
case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */
case SS_U_d: select_bits = 0x3333; break;
/* SunOS 4 only (others have 'g' enabled all the time) */
case 0: select_bits = 0x0202; break;
case SS_B_a: select_bits = 0x0303; break;
case SS_B_x : select_bits = 0x2222; break;
case SS_B_x | SS_B_a: select_bits = 0x3333; break;
/* General BSD options */
case SS_B_g : select_bits = 0x0a0a; break;
case SS_B_g | SS_B_a: select_bits = 0x0f0f; break;
case SS_B_g | SS_B_x : select_bits = 0xaaaa; break;
case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */
all_processes = 1;
simple_select = 0;
break;
default:
return "Process selection options conflict.";
break;
}
return NULL;
}
/***** selected by simple option? */
static int table_accept(proc_t *buf){
unsigned proc_index;
proc_index = (has_our_euid(buf) <<0)
| (session_leader(buf) <<1)
| (without_a_tty(buf) <<2)
| (on_our_tty(buf) <<3);
return (select_bits & (1<<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 short )(buf->foo) == (unsigned short)(*(sn->u+i)).bar) \
return 1
break; case SEL_RUID: return_if_match(ruid,uid);
break; case SEL_EUID: return_if_match(euid,uid);
break; case SEL_SUID: return_if_match(suid,uid);
break; case SEL_FUID: return_if_match(fuid,uid);
break; case SEL_RGID: return_if_match(rgid,gid);
break; case SEL_EGID: return_if_match(egid,gid);
break; case SEL_SGID: return_if_match(sgid,gid);
break; case SEL_FGID: return_if_match(fgid,gid);
break; case SEL_PGRP: return_if_match(pgrp,pid);
break; case SEL_PID : return_if_match(pid,pid);
break; case SEL_TTY : return_if_match(tty,tty);
break; case SEL_SESS: return_if_match(session,pid);
/* TODO Should use a long long cast for performance */
break; case SEL_COMM: i=sn->n; while(i--)
if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 8 )) return 1;
#undef return_if_match
}
sn = sn->next;
}
return 0;
}
/***** This must satisfy Unix98 and as much BSD as possible */
int want_this_proc(proc_t *buf){
int accepted_proc = 1; /* assume success */
/* elsewhere, convert T to list, U sets x implicitly */
/* handle -e -A */
if(all_processes) goto finish;
/* use table for -a a d g x */
if((simple_select || !selection_list))
if(table_accept(buf)) goto finish;
/* search lists */
if(proc_was_listed(buf)) goto finish;
/* fail, fall through to loose ends */
accepted_proc = 0;
/* do r N */
finish:
if(running_only && !running(buf)) accepted_proc = 0;
if(negate_selection) return !accepted_proc;
return accepted_proc;
}

875
ps/sortformat.c Normal file
View File

@ -0,0 +1,875 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <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 "common.h"
static sf_node *sf_list = NULL; /* deferred sorting and formatting */
static int broken; /* use gross Unix98 parsing? */
static int have_gnu_sort = 0; /* if true, "O" must be format */
static int already_parsed_sort = 0; /* redundantly set in & out of fn */
static int already_parsed_format = 0;
#define parse_sort_opt <-- arrgh! do not use this -->
#define gnusort_parse <-- arrgh! do not use this -->
/**************** Parse single format specifier *******************/
static format_node *do_one_spec(const char *spec, const char *override){
const format_struct *fs;
const macro_struct *ms;
fs = search_format_array(spec);
if(fs){
int w1, w2;
format_node *thisnode;
thisnode = malloc(sizeof(format_node));
w1 = fs->width;
if(override){
w2 = strlen(override);
thisnode->width = (w1>w2)?w1:w2;
thisnode->name = malloc(strlen(override)+1);
strcpy(thisnode->name, override);
}else{
thisnode->width = w1;
thisnode->name = malloc(strlen(fs->head)+1);
strcpy(thisnode->name, fs->head);
}
thisnode->pr = fs->pr;
thisnode->pad = fs->pad;
thisnode->vendor = fs->vendor;
thisnode->flags = fs->flags;
thisnode->next = NULL;
return thisnode;
}
/* That failed, so try it as a macro. */
ms = search_macro_array(spec);
if(ms){
format_node *list = NULL;
format_node *newnode;
const char *walk;
int dist;
char buf[16]; /* trust strings will be short (from above, not user) */
walk = ms->head;
while(*walk){
dist = strcspn(walk, ", ");
strncpy(buf,walk,dist);
buf[dist] = '\0';
newnode = do_one_spec(buf,override); /* call self, assume success */
newnode->next = list;
list = newnode;
walk += dist;
if(*walk) walk++;
}
return list;
}
return NULL; /* bad, spec not found */
}
/************ must wrap user format in default *************/
static void O_wrap(sf_node *sfn, int otype){
format_node *fnode;
format_node *endp;
char *trailer;
trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
fnode = do_one_spec("pid",NULL);
if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
endp->next = fnode;
fnode = do_one_spec(trailer,NULL);
if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
endp = fnode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
}
/******************************************************************
* Used to parse option AIX field descriptors.
* Put each completed format_node onto the list starting at ->f_cooked
*/
static const char *aix_format_parse(sf_node *sfn){
char *buf; /* temp copy of arg to hack on */
char *walk;
int items;
/*** sanity check and count items ***/
items = 0;
walk = sfn->sf;
/* state machine */ {
int c;
initial:
c = *walk++;
if(c=='%') goto get_desc;
if(!c) goto looks_ok;
/* get_text: */
items++;
get_more_text:
c = *walk++;
if(c=='%') goto get_desc;
if(c) goto get_more_text;
goto looks_ok;
get_desc:
items++;
c = *walk++;
if(c) goto initial;
return "Improper AIX field descriptor.";
looks_ok:
}
/*** sanity check passed ***/
buf = malloc(strlen(sfn->sf)+1);
strcpy(buf, sfn->sf);
walk = sfn->sf;
while(items--){
format_node *fnode; /* newly allocated */
format_node *endp; /* for list manipulation */
if(*walk == '%'){
const aix_struct *aix;
walk++;
if(*walk == '%') goto double_percent;
aix = search_aix_array(*walk);
walk++;
if(!aix){
free(buf);
return "Unknown AIX field descriptor.";
}
fnode = do_one_spec(aix->spec, aix->head);
if(!fnode){
free(buf);
return "AIX field descriptor processing bug.";
}
} else {
int len;
len = strcspn(walk, "%");
memcpy(buf,walk,len);
if(0){
double_percent:
len = 1;
buf[0] = '%';
}
buf[len] = '\0';
walk += len;
fnode = malloc(sizeof(format_node));
fnode->width = len;
fnode->name = malloc(len+1);
strcpy(fnode->name, buf);
fnode->pr = NULL; /* checked for */
fnode->pad = 0;
fnode->vendor = AIX;
fnode->flags = 0;
fnode->next = NULL;
}
endp = fnode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
}
free(buf);
already_parsed_format = 1;
return NULL;
}
/***************************************************************
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* The "broken" flag enables a really bad Unix98 misfeature.
* Put each completed format_node onto the list starting at ->f_cooked
*/
static const char *format_parse(sf_node *sfn){
char *buf; /* temp copy of arg to hack on */
char *sep_loc; /* separator location: " \t,\n" */
char *walk;
const char *err; /* error code that could or did happen */
format_node *fnode;
int items;
int need_item;
static char errbuf[80]; /* for variable-text error message */
/*** prepare to operate ***/
buf = malloc(strlen(sfn->sf)+1);
strcpy(buf, sfn->sf);
/*** sanity check and count items ***/
need_item = 1; /* true */
items = 0;
walk = buf;
do{
switch(*walk){
case ' ': case ',': case '\t': case '\n': case '\0':
/* Linux extension: allow \t and \n as delimiters */
if(need_item){
free(buf);
goto improper;
}
need_item=1;
break;
case '=':
if(broken) goto out;
/* fall through */
default:
if(need_item) items++;
need_item=0;
}
} while (*++walk);
out:
if(!items){
free(buf);
goto empty;
}
#ifdef STRICT_LIST
if(need_item){ /* can't have trailing deliminator */
free(buf);
goto improper;
}
#else
if(need_item){ /* allow 1 trailing deliminator */
*--walk='\0'; /* remove the trailing deliminator */
}
#endif
/*** actually parse the list ***/
walk = buf;
while(items--){
format_node *endp;
char *equal_loc;
sep_loc = strpbrk(walk," ,\t\n");
/* if items left, then sep_loc is not in header override */
if(items && sep_loc) *sep_loc = '\0';
equal_loc = strpbrk(walk,"=");
if(equal_loc){ /* if header override */
*equal_loc = '\0';
equal_loc++;
}
fnode = do_one_spec(walk,equal_loc);
if(!fnode){
if(!*errbuf){ /* if didn't already create an error string */
snprintf(
errbuf,
sizeof(errbuf),
"Unknown user-defined format specifier \"%s\".",
walk
);
}
free(buf);
goto unknown;
}
endp = fnode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
walk = sep_loc + 1; /* point to next item, if any */
}
free(buf);
already_parsed_format = 1;
return NULL;
/* errors may cause a retry looking for AIX format codes */
if(0) unknown: err=errbuf;
if(0) empty: err="Empty format list.";
if(0) improper: err="Improper format list.";
if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
return err;
}
/**************** Parse single sort specifier *******************/
static sort_node *do_one_sort_spec(const char *spec){
const format_struct *fs;
int reverse = 0;
if(*spec == '-'){
reverse = 1;
spec++;
}
fs = search_format_array(spec);
if(fs){
sort_node *thisnode;
thisnode = malloc(sizeof(format_node));
thisnode->sr = fs->sr;
thisnode->reverse = reverse;
thisnode->next = NULL;
return thisnode;
}
return NULL; /* bad, spec not found */
}
/**************************************************************
* Used to parse long sorting options.
* Put each completed sort_node onto the list starting at ->s_cooked
*/
static const char *long_sort_parse(sf_node *sfn){
char *buf; /* temp copy of arg to hack on */
char *sep_loc; /* separator location: " \t,\n" */
char *walk;
const char *err; /* error code that could or did happen */
sort_node *snode;
int items;
int need_item;
/*** prepare to operate ***/
buf = malloc(strlen(sfn->sf)+1);
strcpy(buf, sfn->sf);
/*** sanity check and count items ***/
need_item = 1; /* true */
items = 0;
walk = buf;
err = "Improper sort specifier list.";
do{
switch(*walk){
case ' ': case ',': case '\t': case '\n': case '\0':
if(need_item){
free(buf);
return "Improper sort list";
}
need_item=1;
break;
default:
if(need_item) items++;
need_item=0;
}
} while (*++walk);
if(!items){
free(buf);
return "Empty sort list.";
}
#ifdef STRICT_LIST
if(need_item){ /* can't have trailing deliminator */
free(buf);
return "Improper sort list.";
}
#else
if(need_item){ /* allow 1 trailing deliminator */
*--walk='\0'; /* remove the trailing deliminator */
}
#endif
/*** actually parse the list ***/
walk = buf;
while(items--){
sort_node *endp;
sep_loc = strpbrk(walk," ,\t\n");
if(sep_loc) *sep_loc = '\0';
snode = do_one_sort_spec(walk);
if(!snode){
free(buf);
return "Unknown sort specifier.";
}
endp = snode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->s_cooked;
sfn->s_cooked = snode;
walk = sep_loc + 1; /* point to next item, if any */
}
free(buf);
already_parsed_sort = 1;
return NULL;
}
/************ pre-parse short sorting option *************/
/* Errors _must_ be detected so that the "O" option can try to
* reparse as formatting codes.
*/
static const char *verify_short_sort(const char *arg){
const char *all = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
char checkoff[256];
int i;
const char *walk;
int tmp;
if(strspn(arg,all) != strlen(arg)) return "Bad sorting code.";
for(i=256; i--;) checkoff[i] = 0;
walk = arg;
for(;;){
tmp = *walk;
switch(tmp){
case '\0':
return NULL; /* looks good */
case '+':
case '-':
tmp = *(walk+1);
if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code.";
break;
case 'P':
if(forest_type) return "PPID sort and forest output conflict.";
/* fall through */
default:
if(checkoff[tmp]) return "Bad sorting code."; /* repeated */
/* ought to check against already accepted sort options */
checkoff[tmp] = 1;
break;
}
walk++;
}
}
/************ parse short sorting option *************/
static const char *short_sort_parse(sf_node *sfn){
int direction = 0;
const char *walk;
int tmp;
sort_node *snode;
sort_node *endp;
const struct shortsort_struct *ss;
walk = sfn->sf;
for(;;){
tmp = *walk;
switch(tmp){
case '\0':
already_parsed_sort = 1;
return NULL;
case '+':
direction = 0;
break;
case '-':
direction = 1;
break;
default:
ss = search_shortsort_array(tmp);
if(!ss) return "Unknown sort specifier.";
snode = do_one_sort_spec(ss->spec);
if(!snode) return "Unknown sort specifier.";
snode->reverse = direction;
endp = snode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->s_cooked;
sfn->s_cooked = snode;
direction = 0;
break;
}
walk++;
}
}
/******************* high-level below here *********************/
/*
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* The "broken" flag enables a really bad Unix98 misfeature.
* Recursion is to preserve original order.
*/
static const char *parse_O_option(sf_node *sfn){
const char *err; /* error code that could or did happen */
if(sfn->next){
err = parse_O_option(sfn->next);
if(err) return err;
}
switch(sfn->sf_code){
case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
err = format_parse(sfn);
if(!err) already_parsed_format = 1;
break;
case SF_U_O: /*** format ***/
/* Can have -l -f f u... set already_parsed_format like DEC does */
if(already_parsed_format) return "option -O can not follow other format options.";
err = format_parse(sfn);
if(err) return err;
already_parsed_format = 1;
O_wrap(sfn,'u'); /* must wrap user format in default */
break;
case SF_B_O: /*** both ***/
if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options.";
else err = verify_short_sort(sfn->sf);
if(!err){ /* success as sorting code */
short_sort_parse(sfn);
already_parsed_sort = 1;
return NULL;
}
if(already_parsed_format){
err = "option O is neither first format nor sort order.";
break;
}
if(!format_parse(sfn)){ /* if success as format code */
already_parsed_format = 1;
O_wrap(sfn,'b'); /* must wrap user format in default */
return NULL;
}
break;
case SF_G_sort: case SF_B_m: /*** sort ***/
if(already_parsed_sort) err = "Multiple sort options.";
else err = long_sort_parse(sfn);
already_parsed_sort = 1;
break;
default: /*** junk ***/
return "Bug: parse_O_option got weirdness!";
}
return err; /* could be NULL */
}
/************ Main parser calls this to save lists for later **********/
/* store data for later and return 1 if arg looks non-standard */
int defer_sf_option(const char *arg, int source){
sf_node *sfn;
char buf[16];
int dist;
const format_struct *fs;
int need_item = 1;
sfn = malloc(sizeof(sf_node));
sfn->sf = malloc(strlen(arg)+1);
strcpy(sfn->sf, arg);
sfn->sf_code = source;
sfn->s_cooked = NULL;
sfn->f_cooked = NULL;
sfn->next = sf_list;
sf_list = sfn;
if(source == SF_G_sort) have_gnu_sort = 1;
/* Now try to find an excuse to ignore broken Unix98 parsing. */
if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
do{
switch(*arg){
case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */
if(need_item) return 1; /* something wrong */
need_item=1;
break;
case '=':
if(need_item) return 1; /* something wrong */
return 0; /* broken Unix98 parsing is required */
default:
if(!need_item) break;
need_item=0;
dist = strcspn(arg,", =");
if(dist>15) return 1; /* something wrong, sort maybe? */
strncpy(buf,arg,dist); /* no '\0' on end */
buf[dist] = '\0'; /* fix that problem */
fs = search_format_array(buf);
if(!fs) return 1; /* invalid spec, macro or sort maybe? */
if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
}
} while (*++arg);
return 0; /* boring, Unix98 is no change */
}
/***** Since ps is not long-lived, the memory leak can be ignored. ******/
void reset_sortformat(void){
sf_list = NULL; /* deferred sorting and formatting */
format_list = NULL; /* digested formatting options */
sort_list = NULL; /* digested sorting options (redundant?) */
have_gnu_sort = 0;
already_parsed_sort = 0;
already_parsed_format = 0;
}
/***** Search format_list for findme, then insert putme after findme. ****/
static int fmt_add_after(const char *findme, format_node *putme){
format_node *walk;
if(!strcmp(format_list->name, findme)){
putme->next = format_list->next;
format_list->next = putme;
return 1; /* success */
}
walk = format_list;
while(walk->next){
if(!strcmp(walk->next->name, findme)){
putme->next = walk->next->next;
walk->next->next = putme;
return 1; /* success */
}
walk = walk->next;
}
return 0; /* fail */
}
/******* Search format_list for findme, then delete it. ********/
static int fmt_delete(const char *findme){
format_node *walk;
format_node *old;
if(!strcmp(format_list->name, findme)){
old = format_list;
format_list = format_list->next;
free(old);
return 1; /* success */
}
walk = format_list;
while(walk->next){
if(!strcmp(walk->next->name, findme)){
old = walk->next;
walk->next = walk->next->next;
free(old);
return 1; /* success */
}
walk = walk->next;
}
return 0; /* fail */
}
/************ Build a SysV format backwards. ***********/
#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
static const char *generate_sysv_list(void){
format_node *fn;
if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
return "Modifier -y without format -l makes no sense.";
if(prefer_bsd_defaults){
if(format_flags) PUSH("cmd");
else PUSH("args");
PUSH("bsdtime");
if(!(format_flags & FF_Ul)) PUSH("stat");
}else{
if(format_flags & FF_Uf) PUSH("cmd");
else PUSH("ucmd");
PUSH("time");
}
PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */
if(format_flags & FF_Uf) PUSH("stime");
/* avoid duplicate columns from -FP and -Fly */
if(format_modifiers & FM_F){
/* if -FP take the Sun-style column instead (sorry about "sgi_p") */
if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */
/* if -Fly take the ADDR-replacement RSS instead */
if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
}
if(format_flags & FF_Ul){
PUSH("wchan");
}
/* since FM_y adds RSS anyway, don't do this hack when that is true */
if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
PUSH("sgi_rss");
fn = malloc(sizeof(format_node));
fn->width = 1;
fn->name = malloc(2);
strcpy(fn->name, ":");
fn->pr = NULL; /* checked for */
fn->pad = 0;
fn->vendor = AIX; /* yes, for SGI weirdness */
fn->flags = 0;
fn->next = format_list;
format_list=fn;
}
}
if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
PUSH("sz");
}
if(format_flags & FF_Ul){
if(format_modifiers & FM_y) PUSH("rss");
else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
else PUSH("addr_1");
}
if(format_modifiers & FM_c){
PUSH("pri"); PUSH("class");
}else if(format_flags & FF_Ul){
PUSH("ni"); PUSH("opri");
}
if((format_modifiers & FM_L) && (format_flags & FF_Uf)) PUSH("nlwp");
if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
if(format_modifiers & FM_P) PUSH("psr");
if(format_modifiers & FM_L) PUSH("lwp");
if(format_modifiers & FM_j){
PUSH("sid");
PUSH("pgid");
}
if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
if(format_modifiers & FM_T) PUSH("spid");
PUSH("pid");
if(format_flags & FF_Uf){
if(personality & PER_SANE_USER) PUSH("user");
else PUSH("uid_hack");
}else if(format_flags & FF_Ul){
PUSH("uid");
}
if(format_flags & FF_Ul){
PUSH("s");
if(!(format_modifiers & FM_y)) PUSH("f");
}
if(format_modifiers & FM_M){
PUSH("label"); /* Mandatory Access Control */
}
format_modifiers = 0;
return NULL;
}
/**************************************************************************
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* The "broken" flag enables a really bad Unix98 misfeature.
*/
const char *process_sf_options(int localbroken){
const char *err;
sf_node *sf_walk;
int option_source; /* true if user-defined */
if(personality & PER_BROKEN_o) localbroken = 1;
if(personality & PER_GOOD_o) localbroken = 0;
broken = localbroken;
if(sf_list){
err = parse_O_option(sf_list);
if(err) return err;
}
if(format_list) printf("Bug: must reset the list first!\n");
/* merge formatting info of sf_list into format_list here */
sf_walk = sf_list;
while(sf_walk){
format_node *fmt_walk;
fmt_walk = sf_walk->f_cooked;
sf_walk->f_cooked = NULL;
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
sf_walk = sf_walk->next;
}
/* merge sorting info of sf_list into sort_list here */
sf_walk = sf_list;
while(sf_walk){
sort_node *srt_walk;
srt_walk = sf_walk->s_cooked;
sf_walk->s_cooked = NULL;
while(srt_walk){ /* put any nodes onto sort_list in opposite way */
sort_node *travler;
travler = srt_walk;
srt_walk = srt_walk->next;
travler->next = sort_list;
sort_list = travler;
}
sf_walk = sf_walk->next;
}
if(format_list){
if(format_flags) return "Conflicting format options.";
option_source = 1;
}else{
format_node *fmt_walk;
format_node *fn;
const char *spec;
switch(format_flags){
default: return "Conflicting format options.";
/* These can be NULL, which enables SysV list generation code. */
case 0: spec=NULL; break;
case FF_Uf | FF_Ul: spec=sysv_fl_format; break;
case FF_Uf: spec=sysv_f_format; break;
case FF_Ul: spec=sysv_l_format; break;
/* These are NOT REACHED for normal -j processing. */
case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */
case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */
case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */
/* These are true BSD options. */
case FF_Bj: spec=bsd_j_format; break;
case FF_Bl: spec=bsd_l_format; break;
case FF_Bs: spec=bsd_s_format; break;
case FF_Bu: spec=bsd_u_format; break;
case FF_Bv: spec=bsd_v_format; break;
/* These are old Linux options. Option m is overloaded. */
case FF_LX: spec="OL_X"; break;
case FF_Lm: spec="OL_m"; break;
} /* end switch(format_flags) */
option_source = 0;
if(!format_flags && !format_modifiers){ /* was default */
char *tmp;
tmp = getenv("PS_FORMAT"); /* user override kills default */
if(tmp && *tmp){
spec = tmp;
option_source = 2;
}
}
if(spec){
fn = do_one_spec(spec, NULL); /* use override "" for no headers */
fmt_walk = fn;
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
}else{
err = generate_sysv_list();
if(err) return err;
option_source = 3;
}
}
if(format_modifiers){ /* generate_sysv_list() may have cleared some bits */
format_node *fn;
if(option_source) return "Can't use output modifiers with user-defined output";
if(format_modifiers & FM_j){
fn = do_one_spec("pgid", NULL);
if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
return "Internal error, no PID or PPID for -j option.";
fn = do_one_spec("sid", NULL);
if(!fmt_add_after("PGID", fn)) return "Lost my PGID!";
}
if(format_modifiers & FM_y){
/* TODO: check for failure to do something, and complain if so */
fmt_delete("F");
fn = do_one_spec("rss", NULL);
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
}
if(format_modifiers & FM_c){
fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
fmt_delete("NI");
fn = do_one_spec("class", NULL);
if(!fmt_add_after("PRI", fn))
return "Internal error, no PRI for -c option.";
fmt_delete("PRI"); /* we want a different one */
fn = do_one_spec("pri", NULL);
if(!fmt_add_after("CLS", fn)) return "Lost my CLS!";
}
}
if(!option_source){ /* OK to really muck with stuff */
format_node *fn;
/* Do personality-specific translations not covered by format_flags.
* Generally, these only get hit when personality overrides unix output.
* That (mostly?) means the Digital and Debian personalities.
*/
if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
fn = do_one_spec("sgi_p", NULL);
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
}
if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
fn = do_one_spec("user", NULL);
if(fmt_add_after("UID", fn)) fmt_delete("UID");
}
}
/* Could scan for duplicates (format and sort) here. Digital does. */
return NULL;
}

165
ps/stacktrace.c Normal file
View File

@ -0,0 +1,165 @@
/*
* Gnu debugger stack trace code provided by Peter Mattis
* <petm@CSUA.Berkeley.EDU> on Thu, 2 Nov 1995
*
* Modified for easy use by Albert Cahalan.
*/
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#define INTERACTIVE 0
#define STACK_TRACE 1
char *stored_prog_name = "You forgot to set \"program\"";
static int stack_trace_done;
/***********/
static void debug_stop(char **args){
execvp (args[0], args);
perror ("exec failed");
_exit (0);
}
/***********/
static void stack_trace_sigchld(int signum){
stack_trace_done = 1;
}
/************/
static void stack_trace(char **args){
pid_t pid;
int in_fd[2];
int out_fd[2];
fd_set fdset;
fd_set readset;
struct timeval tv;
int sel, index, state;
char buffer[256];
char c;
stack_trace_done = 0;
signal(SIGCHLD, stack_trace_sigchld);
if((pipe (in_fd) == -1) || (pipe (out_fd) == -1)){
perror ("could open pipe");
_exit (0);
}
pid = fork ();
if (pid == 0){
close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
execvp (args[0], args); /* exec gdb */
perror ("exec failed");
_exit (0);
} else {
if(pid == (pid_t) -1){
perror ("could not fork");
_exit (0);
}
}
FD_ZERO (&fdset);
FD_SET (out_fd[0], &fdset);
write (in_fd[1], "backtrace\n", 10);
write (in_fd[1], "p x = 0\n", 8);
write (in_fd[1], "quit\n", 5);
index = 0;
state = 0;
for(;;){
readset = fdset;
tv.tv_sec = 1;
tv.tv_usec = 0;
sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
if (sel == -1) break;
if((sel > 0) && (FD_ISSET (out_fd[0], &readset))){
if(read (out_fd[0], &c, 1)){
switch(state){
case 0:
if(c == '#'){
state = 1;
index = 0;
buffer[index++] = c;
}
break;
case 1:
buffer[index++] = c;
if((c == '\n') || (c == '\r')){
buffer[index] = 0;
fprintf (stderr, "%s", buffer);
state = 0;
index = 0;
}
break;
default:
break;
}
}
}
else if(stack_trace_done) break;
}
close (in_fd[0]);
close (in_fd[1]);
close (out_fd[0]);
close (out_fd[1]);
_exit (0);
}
/************/
void debug(int method, char *prog_name){
pid_t pid;
char buf[16];
char *args[4] = { "gdb", NULL, NULL, NULL };
int x;
snprintf (buf, 99, "%d", getpid ());
args[1] = prog_name;
args[2] = buf;
pid = fork ();
if(pid == 0){
switch (method){
case INTERACTIVE:
fprintf (stderr, "debug_stop\n");
debug_stop(args);
break;
case STACK_TRACE:
fprintf (stderr, "stack_trace\n");
stack_trace(args);
break;
}
_exit(0);
} else if(pid == (pid_t) -1){
perror ("could not fork");
return;
}
x = 1;
while(x); /* wait for debugger? */
}
/************/
static void stack_trace_sigsegv(int signum){
debug(STACK_TRACE, stored_prog_name);
}
/************/
void init_stack_trace(char *prog_name){
stored_prog_name = prog_name;
signal(SIGSEGV, stack_trace_sigsegv);
}

118
skill.1 Normal file
View File

@ -0,0 +1,118 @@
,\" t
.\" (The preceding line is a note to broken versions of man to tell
.\" them to pre-process this man page with tbl)
.\" Man page for skill and snice.
.\" Licensed under version 2 of the GNU General Public License.
.\" Written by Albert Cahalan, converted to a man page by
.\" Michael K. Johnson
.\"
.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual"
.SH NAME
skill, snice \- report process status
.SH SYNOPSIS
.nf
skill [signal to send] [options] process selection criteria
snice [new priority] [options] process selection criteria
.fi
.SH DESCRIPTION
The default signal for skill is TERM. Use -l or -L to list available signals.
Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
The default priority for snice is +4. (snice +4 ...)
Priority numbers range from +20 (slowest) to -20 (fastest).
Negative priority numbers are restricted to administrative users.
.SH "GENERAL OPTIONS"
.TS
l l l.
-f fast mode This is not currently useful.
-i interactive use T{
You will be asked to approve each action.
T}
-v verbose output T{
Display information about selected processes.
T}
-w warnings enabled This is not currently useful.
-n no action This only displays the process ID.
.TE
.SH "PROCESS SELECTION OPTIONS"
Selection criteria can be: terminal, user, pid, command.
The options below may be used to ensure correct interpretation.
.TS
l l.
-t The next argument is a terminal (tty or pty).
-u The next argument is a username.
-p The next argument is a process ID number.
-c The next argument is a command name.
.TE
.SH SIGNALS
The signals listed below may be available for use with skill.
When known, numbers and default behavior are shown.
.TS
lB rB lB lB
lfCW r l l.
Name Num Action Description
.TH
ALRM 14 exit
HUP 1 exit
INT 2 exit
KILL 9 exit this signal may not be blocked
PIPE 13 exit
POLL exit
PROF exit
TERM 15 exit
USR1 exit
USR2 exit
VTALRM exit
STKFLT exit may not be implemented
PWR ignore may exit on some systems
WINCH ignore
CHLD ignore
URG ignore
TSTP stop may interact with the shell
TTIN stop may interact with the shell
TTOU stop may interact with the shell
STOP stop this signal may not be blocked
CONT restart continue if stopped, otherwise ignore
ABRT 6 core
FPE 8 core
ILL 4 core
QUIT 3 core
SEGV 11 core
TRAP 5 core
SYS core may not be implemented
EMT core may not be implemented
BUS core core dump may fail
XCPU core core dump may fail
XFSZ core core dump may fail
.TE
.SH EXAMPLES
.TS
lB lB
lfCW l.
Command Description
.TC
snice netscape crack +7 Slow down netscape and crack
skill -KILL -v /dev/pts/* Kill users on new-style PTY devices
skill -STOP torvalds davem tytso Stop 3 users
snice -17 root bash Give priority to root's shell
.TE
.SH "SEE ALSO"
top(1) kill(1) renice(1) nice(1)
.SH STANDARDS
No standards apply.
.SH AUTHOR
Albert Cahalan <acahalan@cs.uml.edu> wrote skill and snice in 1999 as a
replacement for a non-free version. Michael K. Johnson <johnsonm@redhat.com>
is the current maintainer of the procps collection.
Please send bug reports to <acahalan@cs.uml.edu>

557
skill.c Normal file
View File

@ -0,0 +1,557 @@
/*
* Copyright 1998 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <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/sig.h>
#include <proc/devname.h>
#include <proc/procps.h> /* char *user_from_uid(uid_t uid) */
static int f_flag, i_flag, v_flag, w_flag, n_flag;
static int tty_count, uid_count, cmd_count, pid_count;
static int *ttys;
static uid_t *uids;
static char **cmds;
static int *pids;
#define ENLIST(thing,addme) do{ \
if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \
if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \
thing##s[thing##_count++] = addme; \
}while(0)
static int my_pid;
static int saved_argc;
static char **saved_argv;
static int sig_or_pri;
static int program;
#define PROG_GARBAGE 0 /* keep this 0 */
#define PROG_KILL 1
#define PROG_SKILL 2
/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */
#define PROG_SNICE 4
/********************************************************************/
/***** kill or nice a process */
static void hurt_proc(int tty, int uid, int pid, char *cmd){
int failed;
int saved_errno;
char dn_buf[1000];
dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
if(i_flag){
char buf[8];
fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s ? ",
(char*)dn_buf,user_from_uid(uid),pid,cmd
);
if(!fgets(buf,7,stdin)){
printf("\n");
exit(0);
}
if(*buf!='y' && *buf!='Y') return;
}
/* do the actual work */
if(program==PROG_SKILL) failed=kill(pid,sig_or_pri);
else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri);
saved_errno = errno;
if(w_flag && failed){
fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s ",
(char*)dn_buf,user_from_uid(uid),pid,cmd
);
errno = saved_errno;
perror("");
return;
}
if(i_flag) return;
if(v_flag){
printf("%-8.8s %-8.8s %5d %-16.16s\n",
(char*)dn_buf,user_from_uid(uid),pid,cmd
);
return;
}
if(n_flag){
printf("%d\n",pid);
return;
}
}
/***** check one process */
static void check_proc(int pid){
char buf[128];
struct stat statbuf;
char *tmp;
int tty;
int fd;
int i;
if(pid==my_pid) return;
sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
fd = open(buf,O_RDONLY);
if(fd==-1){ /* process exited maybe */
if(pids && w_flag) printf("WARNING: process %d could not be found.",pid);
return;
}
fstat(fd, &statbuf);
if(uids){ /* check the EUID */
i=uid_count;
while(i--) if(uids[i]==statbuf.st_uid) break;
if(i==-1) goto closure;
}
read(fd,buf,128);
buf[127] = '\0';
tmp = strrchr(buf, ')');
*tmp++ = '\0';
i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */
tty = atoi(tmp);
if(ttys){
i=tty_count;
while(i--) if(ttys[i]==tty) break;
if(i==-1) goto closure;
}
tmp = strchr(buf, '(') + 1;
if(cmds){
i=cmd_count;
/* fast comparison trick -- useful? */
while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break;
if(i==-1) goto closure;
}
/* This is where we kill/nice something. */
/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
pid, statbuf.st_uid, tty>>8, tty&0xf, tmp
);
*/
hurt_proc(tty, statbuf.st_uid, pid, tmp);
closure:
close(fd); /* kill/nice _first_ to avoid PID reuse */
}
/***** debug function */
#if 0
static void show_lists(void){
int i;
fprintf(stderr, "%d TTY: ", tty_count);
if(ttys){
i=tty_count;
while(i--){
fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n');
}
}else fprintf(stderr, "\n");
fprintf(stderr, "%d UID: ", uid_count);
if(uids){
i=uid_count;
while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n');
}else fprintf(stderr, "\n");
fprintf(stderr, "%d PID: ", pid_count);
if(pids){
i=pid_count;
while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n');
}else fprintf(stderr, "\n");
fprintf(stderr, "%d CMD: ", cmd_count);
if(cmds){
i=cmd_count;
while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n');
}else fprintf(stderr, "\n");
}
#endif
/***** iterate over all PIDs */
static void iterate(void){
int pid;
DIR *d;
struct dirent *de;
if(pids){
pid = pid_count;
while(pid--) check_proc(pids[pid]);
return;
}
#if 0
/* could setuid() and kill -1 to have the kernel wipe out a user */
if(!ttys && !cmds && !pids && !i_flag){
}
#endif
d = opendir("/proc");
if(!d){
perror("/proc");
exit(1);
}
while(( de = readdir(d) )){
if(de->d_name[0] > '9') continue;
if(de->d_name[0] < '1') continue;
pid = atoi(de->d_name);
if(pid) check_proc(pid);
}
closedir (d);
}
/***** kill help */
static void kill_usage(void){
fprintf(stderr,
"Usage:\n"
" kill pid ... Send SIGTERM to every process listed.\n"
" kill signal pid ... Send a signal to every process listed.\n"
" kill -s signal pid ... Send a signal to every process listed.\n"
" kill -l List all signal names.\n"
" kill -L List all signal names in a nice table.\n"
" kill -l signal Convert between signal numbers and names.\n"
);
exit(1);
}
/***** kill */
static void kill_main(int argc, char *argv[]){
char *sigptr;
int signo = SIGTERM;
int exitvalue = 0;
if(argc<2) kill_usage();
if(argv[1][0]!='-'){
argv++;
argc--;
goto no_more_args;
}
/* The -l option prints out signal names. */
if(argv[1][1]=='l' && argv[1][2]=='\0'){
if(argc==2){
unix_print_signals();
exit(0);
}
/* at this point, argc must be 3 or more */
if(argc>128 || argv[2][0] == '-') kill_usage();
exit(print_given_signals(argc-2, argv+2, 80));
}
/* The -L option prints out signal names in a nice table. */
if(argv[1][1]=='L' && argv[1][2]=='\0'){
if(argc==2){
pretty_print_signals();
exit(0);
}
kill_usage();
}
if(argv[1][1]=='-' && argv[1][2]=='\0'){
argv+=2;
argc-=2;
goto no_more_args;
}
if(argv[1][1]=='-') kill_usage(); /* likely --help */
if(argv[1][1]=='s' && argv[1][2]=='\0'){
sigptr = argv[2];
argv+=3;
argc-=3;
}else{
sigptr = argv[1]+1;
argv+=2;
argc-=2;
}
signo = signal_name_to_number(sigptr);
if(signo<0){
fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr);
kill_usage();
}
no_more_args:
if(!argc) kill_usage(); /* nothing to kill? */
while(argc--){
long pid;
char *endp;
pid = strtol(argv[argc],&endp,10);
if(!*endp){
if(!kill((pid_t)pid,signo)) continue;
exitvalue = 1;
continue;
}
fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]);
kill_usage();
}
exit(exitvalue);
}
/***** skill/snice help */
static void skillsnice_usage(void){
if(program==PROG_SKILL){
fprintf(stderr,
"Usage: skill [signal to send] [options] process selection criteria\n"
"Example: skill -KILL -v pts/*\n"
"\n"
"The default signal is TERM. Use -l or -L to list available signals.\n"
"Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
"Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"
);
}else{
fprintf(stderr,
"Usage: snice [new priority] [options] process selection criteria\n"
"Example: snice netscape crack +7\n"
"\n"
"The default priority is +4. (snice +4 ...)\n"
"Priority numbers range from +20 (slowest) to -20 (fastest).\n"
"Negative priority numbers are restricted to administrative users.\n"
);
}
fprintf(stderr,
"\n"
"General options:\n"
"-f fast mode This is not currently useful.\n"
"-i interactive use You will be asked to approve each action.\n"
"-v verbose output Display information about selected processes.\n"
"-w warnings enabled This is not currently useful.\n"
"-n no action This only displays the process ID.\n"
"\n"
"Selection criteria can be: terminal, user, pid, command.\n"
"The options below may be used to ensure correct interpretation.\n"
"-t The next argument is a terminal (tty or pty).\n"
"-u The next argument is a username.\n"
"-p The next argument is a process ID number.\n"
"-c The next argument is a command name.\n"
);
exit(1);
}
#if 0
static void _skillsnice_usage(int line){
fprintf(stderr,"Something at line %d.\n", line);
skillsnice_usage();
}
#define skillsnice_usage() _skillsnice_usage(__LINE__)
#endif
#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL)
/***** common skill/snice argument parsing code */
#define NO_PRI_VAL ((int)0xdeafbeef)
static void skillsnice_parse(int argc, char *argv[]){
int signo = -1;
int prino = NO_PRI_VAL;
int force = 0;
int num_found = 0;
char *argptr;
if(argc<2) skillsnice_usage();
if(argc==2 && argv[1][0]=='-'){
if(!strcmp(argv[1],"-L")){
pretty_print_signals();
exit(0);
}
if(!strcmp(argv[1],"-l")){
unix_print_signals();
exit(0);
}
skillsnice_usage();
}
NEXTARG;
/* Time for serious parsing. What does "skill -int 123 456" mean? */
while(argc){
if(force && !num_found){ /* if forced, _must_ find something */
fprintf(stderr,"ERROR: -%c used with bad data.\n", force);
skillsnice_usage();
}
force = 0;
if(program==PROG_SKILL && signo<0 && *argptr=='-'){
signo = signal_name_to_number(argptr+1);
if(signo>=0){ /* found a signal */
if(!NEXTARG) break;
continue;
}
}
if(program==PROG_SNICE && prino==NO_PRI_VAL
&& (*argptr=='+' || *argptr=='-') && argptr[1]){
long val;
char *endp;
val = strtol(argptr,&endp,10);
if(!*endp && val<=999 && val>=-999){
prino=val;
if(!NEXTARG) break;
continue;
}
}
/* If '-' found, collect any flags. (but lone "-" is a tty) */
if(*argptr=='-' && argptr[1]){
argptr++;
do{
switch(( force = *argptr++ )){
default: skillsnice_usage();
case 't':
case 'u':
case 'p':
case 'c':
if(!*argptr){ /* nothing left here, *argptr is '\0' */
if(!NEXTARG){
fprintf(stderr,"ERROR: -%c with nothing after it.\n", force);
skillsnice_usage();
}
}
goto selection_collection;
case 'f': f_flag++; break;
case 'i': i_flag++; break;
case 'v': v_flag++; break;
case 'w': w_flag++; break;
case 'n': n_flag++; break;
case 0:
NEXTARG;
/*
* If no more arguments, all the "if(argc)..." tests will fail
* and the big loop will exit.
*/
} /* END OF SWITCH */
}while(force);
} /* END OF IF */
selection_collection:
num_found = 0; /* we should find at least one thing */
switch(force){ /* fall through each data type */
default: skillsnice_usage();
case 0: /* not forced */
case 't':
if(argc){
struct stat sbuf;
char path[32];
if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */
snprintf(path,32,"/dev/%s",argptr);
if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){
num_found++;
ENLIST(tty,sbuf.st_rdev);
if(!NEXTARG) break;
}else if(!(argptr[1])){ /* if only 1 character */
switch(*argptr){
default:
if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */
case '-':
case '?':
num_found++;
ENLIST(tty,-1);
if(!NEXTARG) break;
}
}
}
if(force) continue;
case 'u':
if(argc){
struct passwd *passwd_data;
passwd_data = getpwnam(argptr);
if(passwd_data){
num_found++;
ENLIST(uid,passwd_data->pw_uid);
if(!NEXTARG) break;
}
}
if(force) continue;
case 'p':
if(argc && *argptr>='0' && *argptr<='9'){
char *endp;
int num;
num = strtol(argptr, &endp, 0);
if(*endp == '\0'){
num_found++;
ENLIST(pid,num);
if(!NEXTARG) break;
}
}
if(force) continue;
if(num_found) continue; /* could still be an option */
case 'c':
if(argc){
num_found++;
ENLIST(cmd,argptr);
if(!NEXTARG) break;
}
} /* END OF SWITCH */
} /* END OF WHILE */
/* No more arguments to process. Must sanity check. */
if(!tty_count && !uid_count && !cmd_count && !pid_count){
fprintf(stderr,"ERROR: no process selection criteria.\n");
skillsnice_usage();
}
if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){
fprintf(stderr,"ERROR: general flags may not be repeated.\n");
skillsnice_usage();
}
if(i_flag && (v_flag|f_flag|n_flag)){
fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n");
skillsnice_usage();
}
if(v_flag && (i_flag|f_flag)){
fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n");
skillsnice_usage();
}
if(n_flag && signo>=0){
fprintf(stderr,"ERROR: -n makes no sense with a signal.\n");
skillsnice_usage();
}
if(n_flag && prino!=NO_PRI_VAL){
fprintf(stderr,"ERROR: -n makes no sense with a priority value.\n");
skillsnice_usage();
}
/* OK, set up defaults */
if(prino==NO_PRI_VAL) prino=4;
if(signo<0) signo=SIGTERM;
if(n_flag){
program=PROG_SKILL;
signo=0; /* harmless */
}
if(program==PROG_SKILL) sig_or_pri = signo;
else sig_or_pri = prino;
}
/***** main body */
int main(int argc, char *argv[]){
char *tmpstr;
my_pid = getpid();
saved_argv = argv;
saved_argc = argc;
if(!argc){
fprintf(stderr,"ERROR: could not determine own name.\n");
exit(1);
}
tmpstr=strrchr(*argv,'/');
if(tmpstr) tmpstr++;
if(!tmpstr) tmpstr=*argv;
program = PROG_GARBAGE;
if(*tmpstr=='s'){
setpriority(PRIO_PROCESS,my_pid,-20);
if(!strcmp(tmpstr,"snice")) program = PROG_SNICE;
if(!strcmp(tmpstr,"skill")) program = PROG_SKILL;
}else{
if(!strcmp(tmpstr,"kill")) program = PROG_KILL;
}
switch(program){
case PROG_SNICE:
case PROG_SKILL:
skillsnice_parse(argc, argv);
/* show_lists(); */
iterate(); /* this is it, go get them */
break;
case PROG_KILL:
kill_main(argc, argv);
break;
default:
fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr);
}
return 0;
}

1
snice.1 Normal file
View File

@ -0,0 +1 @@
.so man1/skill.1

74
sysctl.8 Normal file
View File

@ -0,0 +1,74 @@
.\" Copyright 1999, George Staikos (staikos@0wned.org)
.\" This file may be used subject to the terms and conditions of the
.\" GNU General Public License Version 2, or any later version
.\" at your option, as published by the Free Software Foundation.
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details."
.TH SYSCTL 8 "21 Sep 1999" "" ""
.SH NAME
sysctl \- configure kernel parameters at runtime
.SH SYNOPSIS
.B "sysctl [-n] variable ..."
.br
.B "sysctl [-n] -w variable=value ..."
.br
.B "sysctl [-n] -p <filename> (default /etc/sysctl.conf)"
.br
.B "sysctl [-n] -a"
.br
.B "sysctl [-n] -A"
.SH DESCRIPTION
.B sysctl
is used to modify kernel parameters at runtime. The parameters available
are those listed under /proc/sys/. Procfs is required for
.B sysctl(8)
support in Linux. You can use
.B sysctl(8)
to both read and write sysctl data.
.SH PARAMETERS
.TP
.B "variable"
The name of a key to read from. An example is kernel.ostype. The '/'
separator is also accepted in place of a '.'.
.TP
.B "variable=value"
To set a key, use the form variable=value, where variable is the key and
value is the value to set it to. If the value contains quotes or characters
which are parsed by the shell, you may need to enclose the value in double
quotes. This requires the -w parameter to use.
.TP
.B "-n"
Use this option to disable printing of the key name when printing values.
.TP
.B "-w"
Use this option when you want to change a sysctl setting.
.TP
.B "-p"
Load in sysctl settings from the file specified or /etc/sysctl.conf if none given.
.TP
.B "-a"
Display all values currently available.
.TP
.B "-A"
Display all values currently available in table form.
.SH EXAMPLES
.TP
/sbin/sysctl -a
.TP
/sbin/sysctl -n kernel.hostname
.TP
/sbin/sysctl -w kernel.domainname="example.com"
.TP
/sbin/sysctl -p /etc/sysctl.conf
.SH FILES
.I /proc/sys
.I /etc/sysctl.conf
.SH SEE ALSO
.BR sysctl.conf (5)
.SH BUGS
The -A parameter behaves just as -a does.
.SH AUTHOR
George Staikos, <staikos@0wned.org>

437
sysctl.c Normal file
View File

@ -0,0 +1,437 @@
/*
* Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
*
*
* "Copyright 1999 George Staikos
* This file may be used subject to the terms and conditions of the
* GNU General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details."
*
* Changelog:
* v1.01:
* - added -p <preload> to preload values from a file
*/
#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>
/*
* Additional types we might need.
*/
typedef int bool;
static bool true = 1;
static bool false = 0;
/*
* Function Prototypes
*/
int Usage(const char *name);
void Preload(const char *filename);
int WriteSetting(const char *setting);
int ReadSetting(const char *setting);
int DisplayAll(const char *path, bool ShowTableUtil);
/*
* Globals...
*/
const char *PROC_PATH = "/proc/sys/";
const char *DEFAULT_PRELOAD = "/etc/sysctl.conf";
static bool PrintName;
static bool PrintNewline;
/* error messages */
const char *ERR_UNKNOWN_PARAMETER = "error: Unknown parameter '%s'\n";
const char *ERR_MALFORMED_SETTING = "error: Malformed setting '%s'\n";
const char *ERR_NO_EQUALS = "error: '%s' must be of the form name=value\n";
const char *ERR_INVALID_KEY = "error: '%s' is an unknown key\n";
const char *ERR_UNKNOWN_WRITING = "error: unknown error %d setting key '%s'\n";
const char *ERR_UNKNOWN_READING = "error: unknown error %d reading key '%s'\n";
const char *ERR_PERMISSION_DENIED = "error: permission denied on key '%s'\n";
const char *ERR_OPENING_DIR = "error: unable to open directory '%s'\n";
const char *ERR_PRELOAD_FILE = "error: unable to open preload file '%s'\n";
const char *WARN_BAD_LINE = "warning: %s(%d): invalid syntax, continuing...\n";
#define DOTSLASH(x) do{ \
char *p_ = (x); \
for(;;){ \
p_ = strchr(p_, '.'); \
if(!p_) break; \
*p_ = '/'; \
} \
}while(0)
#define SLASHDOT(x) do{ \
char *p_ = (x); \
for(;;){ \
p_ = strchr(p_, '.'); \
if(!p_) break; \
*p_ = '/'; \
} \
}while(0)
/*
* Main...
*
*/
int main(int argc, char **argv) {
const char *me = (const char *)basename(argv[0]);
bool SwitchesAllowed = true;
bool WriteMode = false;
int ReturnCode = 0;
const char *preloadfile = DEFAULT_PRELOAD;
PrintName = true;
PrintNewline = true;
if (argc < 2) {
return Usage(me);
} /* endif */
argv++;
for (; argv && *argv && **argv; argv++) {
if (SwitchesAllowed && **argv == '-') { /* we have a switch */
switch((*argv)[1]) {
case 'b':
/* This is "binary" format, which means more for BSD. */
PrintNewline = false;
/* FALL THROUGH */
case 'n':
PrintName = false;
break;
case 'w':
SwitchesAllowed = false;
WriteMode = true;
break;
case 'p':
argv++;
if (argv && *argv && **argv) {
preloadfile = *argv;
} /* endif */
Preload(preloadfile);
return(0);
break;
case 'a': /* string and integer values (for Linux, all of them) */
case 'A': /* the above, including "opaques" (would be unprintable) */
case 'X': /* the above, with opaques completly printed in hex */
SwitchesAllowed = false;
return DisplayAll(PROC_PATH, ((*argv)[1] == 'a') ? false : true);
case 'h':
case '?':
return Usage(me);
default:
fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
return Usage(me);
} /* end switch */
} else {
SwitchesAllowed = false;
if (WriteMode)
ReturnCode = WriteSetting(*argv);
else ReadSetting(*argv);
} /* end if */
} /* end for */
return ReturnCode;
} /* end main */
/*
* Display the usage format
*
*/
int Usage(const char *name) {
printf("usage: %s [-n] variable ... \n"
" %s [-n] -w variable=value ... \n"
" %s [-n] -a \n"
" %s [-n] -p <file> (default /etc/sysctl.conf) \n"
" %s [-n] -A\n", name, name, name, name, name);
return -1;
} /* end Usage() */
/*
* Strip the leading and trailing spaces from a string
*
*/
char *StripLeadingAndTrailingSpaces(char *oneline) {
char *t;
if (!oneline || !*oneline)
return oneline;
t = oneline;
t += strlen(oneline)-1;
while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline)
*t-- = 0;
t = oneline;
while ((*t == ' ' || *t == '\t') && *t != 0)
t++;
return t;
} /* end StripLeadingAndTrailingSpaces() */
/*
* Preload the sysctl's from the conf file
* - we parse the file and then reform it (strip out whitespace)
*
*/
void Preload(const char *filename) {
FILE *fp;
char oneline[257];
char buffer[257];
char *t;
int n = 0;
char *name, *value;
if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
fprintf(stderr, ERR_PRELOAD_FILE, filename);
return;
} /* endif */
while (fgets(oneline, 256, fp)) {
oneline[256] = 0;
n++;
t = StripLeadingAndTrailingSpaces(oneline);
if (strlen(t) < 2)
continue;
if (*t == '#' || *t == ';')
continue;
name = strtok(t, "=");
if (!name || !*name) {
fprintf(stderr, WARN_BAD_LINE, filename, n);
continue;
} /* endif */
StripLeadingAndTrailingSpaces(name);
value = strtok(NULL, "\n\r");
if (!value || !*value) {
fprintf(stderr, WARN_BAD_LINE, filename, n);
continue;
} /* endif */
while ((*value == ' ' || *value == '\t') && *value != 0)
value++;
sprintf(buffer, "%s=%s", name, value);
WriteSetting(buffer);
} /* endwhile */
fclose(fp);
} /* end Preload() */
/*
* Write a sysctl setting
*
*/
int WriteSetting(const char *setting) {
int rc = 0;
const char *name = setting;
const char *value;
const char *equals;
char *tmpname;
FILE *fp;
char *outname;
if (!name) { /* probably dont' want to display this err */
return 0;
} /* end if */
equals = index(setting, '=');
if (!equals) {
fprintf(stderr, ERR_NO_EQUALS, setting);
return -1;
} /* end if */
value = equals + sizeof(char); /* point to the value in name=value */
if (!*name || !*value || name == equals) {
fprintf(stderr, ERR_MALFORMED_SETTING, setting);
return -2;
} /* end if */
tmpname = (char *)malloc((equals-name+1+strlen(PROC_PATH))*sizeof(char));
outname = (char *)malloc((equals-name+1)*sizeof(char));
strcpy(tmpname, PROC_PATH);
strncat(tmpname, name, (int)(equals-name));
tmpname[equals-name+strlen(PROC_PATH)] = 0;
strncpy(outname, name, (int)(equals-name));
outname[equals-name] = 0;
DOTSLASH(tmpname); /* change . to / */
SLASHDOT(outname); /* change / to . */
fp = fopen(tmpname, "w");
if (!fp) {
switch(errno) {
case ENOENT:
fprintf(stderr, ERR_INVALID_KEY, outname);
break;
case EACCES:
fprintf(stderr, ERR_PERMISSION_DENIED, outname);
break;
default:
fprintf(stderr, ERR_UNKNOWN_WRITING, errno, outname);
break;
} /* end switch */
rc = -1;
} else {
fprintf(fp, "%s\n", value);
fclose(fp);
if (PrintName) {
fprintf(stdout, "%s = %s\n", outname, value);
} else {
if (PrintNewline)
fprintf(stdout, "%s\n", value);
else
fprintf(stdout, "%s", value);
}
} /* endif */
free(tmpname);
free(outname);
return rc;
} /* end WriteSetting() */
/*
* Read a sysctl setting
*
*/
int ReadSetting(const char *setting) {
int rc = 0;
char *tmpname, *outname;
char inbuf[1025];
const char *name = setting;
FILE *fp;
if (!setting || !*setting) {
fprintf(stderr, ERR_INVALID_KEY, setting);
} /* endif */
tmpname = (char *)malloc((strlen(name)+strlen(PROC_PATH)+1)*sizeof(char));
outname = (char *)malloc((strlen(name)+1)*sizeof(char));
strcpy(tmpname, PROC_PATH);
strcat(tmpname, name);
strcpy(outname, name);
DOTSLASH(tmpname); /* change . to / */
SLASHDOT(outname); /* change / to . */
fp = fopen(tmpname, "r");
if (!fp) {
switch(errno) {
case ENOENT:
fprintf(stderr, ERR_INVALID_KEY, outname);
break;
case EACCES:
fprintf(stderr, ERR_PERMISSION_DENIED, outname);
break;
default:
fprintf(stderr, ERR_UNKNOWN_READING, errno, outname);
break;
} /* end switch */
rc = -1;
} else {
while(fgets(inbuf, 1024, fp)) {
/* already has the \n in it */
if (PrintName) {
fprintf(stdout, "%s = %s", outname, inbuf);
} else {
if (!PrintNewline) {
char *nlptr = strchr(inbuf,'\n');
if(nlptr) *nlptr='\0';
}
fprintf(stdout, "%s", inbuf);
}
} /* endwhile */
fclose(fp);
} /* endif */
free(tmpname);
free(outname);
return rc;
} /* end ReadSetting() */
/*
* Display all the sysctl settings
*
*/
int DisplayAll(const char *path, bool ShowTableUtil) {
int rc = 0;
int rc2;
DIR *dp;
struct dirent *de;
char *tmpdir;
struct stat ts;
dp = opendir(path);
if (!dp) {
fprintf(stderr, ERR_OPENING_DIR, path);
rc = -1;
} else {
readdir(dp); readdir(dp); /* skip . and .. */
while (( de = readdir(dp) )) {
tmpdir = (char *)malloc(strlen(path)+strlen(de->d_name)+2);
sprintf(tmpdir, "%s%s", path, de->d_name);
rc2 = stat(tmpdir, &ts); /* should check this return code */
if (rc2 != 0) {
perror(tmpdir);
} else {
if (S_ISDIR(ts.st_mode)) {
strcat(tmpdir, "/");
DisplayAll(tmpdir, ShowTableUtil);
} else {
rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
} /* endif */
} /* endif */
free(tmpdir);
} /* end while */
closedir(dp);
} /* endif */
return rc;
} /* end DisplayAll() */

51
sysctl.conf.5 Normal file
View File

@ -0,0 +1,51 @@
.\" Copyright 1999, George Staikos (staikos@0wned.org)
.\" This file may be used subject to the terms and conditions of the
.\" GNU General Public License Version 2, or any later version
.\" at your option, as published by the Free Software Foundation.
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details."
.TH SYSCTL.CONF 5 "21 Sep 1999" "" ""
.SH NAME
sysctl.conf \- sysctl(8) preload/configuration file
.SH DESCRIPTION
.I sysctl.conf
is a simple file containing sysctl values to be read in and set by sysctl(8).
The syntax is simply as follows:
.RS
.sp
.nf
.ne 7
# comment
; comment
token = value
.fi
.sp
.RE
.PP
Note that blank lines are ignored, and whitespace before and after a token or
value is ignored, although a value can contain whitespace within. Lines which
begin with a # or ; are considered comments and ignored.
.SH EXAMPLE
.RS
.sp
.nf
.ne 7
# sysctl.conf sample
#
kernel.domainname = example.com
; this one has a space which will be written to the sysctl!
kernel.modprobe = /sbin/mod probe
.fi
.sp
.RE
.PP
.SH SEE ALSO
.BR sysctl(8)
.SH AUTHOR
George Staikos, <staikos@0wned.org>

6
t Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
#
# Wow, using $* causes great pain with embedded spaces in arguments.
# The "$@" won't break that into 2 arguments.
#
LD_PRELOAD=proc/libproc.so exec ./top "$@"

50
tload.1 Normal file
View File

@ -0,0 +1,50 @@
.\" -*-Nroff-*-
.\" This page Copyright (C) 1993 Matt Welsh, mdw@tc.cornell.edu.
.\" Freely distributable under the terms of the GPL
.TH TLOAD 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
.SH NAME
tload \- graphic representation of system load average
.SH SYNOPSIS
.B tload
.RB [ "\-V" "] [" "\-s"
.IR scale "] ["
.BI "\-d" " delay"
.RI "] [" tty ]
.SH DESCRIPTION
\fBtload\fP prints a graph of the current system load average to the
specified \fItty\fP (or the tty of the tload process if none is specified).
.SS Options
The
.BI "\-s" " scale"
option allows a vertical scale to be specified for the
display (in characters between graph ticks); thus, a smaller value
represents a larger scale, and vice versa.
The
.BI "\-d" " delay"
sets the delay between graph updates in seconds.
.PP
.SH FILES
.I /proc/loadavg
load average information
.SH "SEE ALSO"
.BR ps (1),
.BR top (1),
.BR uptime (1),
.BR w (1)
.SH BUGS
The
.BI "\-d" " delay"
option sets the time argument for an
.BR alarm (2);
if -d 0 is specified, the alarm is set to 0, which will never send the
.B SIGALRM
and update the display.
.SH AUTHORS
Branko Lankester, David Engel <david@ods.com>, and
Michael K. Johnson <johnsonm@redhat.com>.
Please send bug reports to <acahalan@cs.uml.edu>

156
tload.c Normal file
View File

@ -0,0 +1,156 @@
/*
* tload.c - terminal version of xload
*
* Options:
* -s initial scaling exponent (default = 6)
* -d delay
*
* Copyright (c) 1992 Branko Lankester
* /proc changes by David Engel (david@ods.com)
* Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu)
*/
#include "proc/version.h"
#include "proc/sysinfo.h"
#include <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();
}
}

730
tmp-junk.c Normal file
View File

@ -0,0 +1,730 @@
/*
* w.c v1.4
*
* An alternative "w" program for Linux.
* Shows users and their processes.
*
* Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant
* bassman@hpbbi30.bbn.hp.com (Old address)
* bassman@muttley.soc.staffs.ac.uk
*
* Info:
* I starting writing as an improvement of the w program included
* with linux. The idea was to add in some extra functionality to the
* program, and see if I could fix a couple of bugs which seemed to
* occur.
* Mr. Bassman, 10/94
*
* Acknowledgments:
*
* The original version of w:
* Copyright (c) 1993 Larry Greenfield (greenfie@gauss.rutgers.edu)
*
* Uptime routine and w mods:
* Michael K. Johnson (johnsonm@stolaf.edu)
*
*
* Distribution:
* This program is freely distributable under the terms of copyleft.
* No warranty, no support, use at your own risk etc.
*
* Compilation:
* gcc -O -o w sysinfo.c whattime.c w.c
*
* Usage:
* w [-hfusd] [user]
*
*
* $Log: tmp-junk.c,v $
* Revision 1.1 2002/02/01 22:46:37 csmall
* Initial revision
*
* Revision 1.5 1994/10/26 17:57:35 bassman
* Loads of stuff - see comments.
*
* Revision 1.4 1994/01/01 12:57:21 johnsonm
* Added RCS, and some other fixes.
*
* Revision history:
* Jan 01, 1994 (mkj): Eliminated GCC warnings, took out unnecessary
* dead variables in fscanf, replacing them with
* *'d format qualifiers. Also added RCS stuff.
* Oct 26, 1994 (bass): Tidied up the code, fixed bug involving corrupt
* utmp records. Added switch for From field;
* default is compile-time set. Added -d option
* as a remnant from BSD 'w'. Fixed bug so it now
* behaves if the first process on a tty isn't owned
* by the person first logged in on that tty, and
* also detects su'd users. Changed the tty format
* to the short one.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <utmp.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include "proc/whattime.h"
#define TRUE 1
#define FALSE 0
/*
* Default setting for whether to have a From field. The -f switch
* toggles this - if the default is to have it, using -f will turn
* it off; if the default is not to have it, the -f switch will put
* it in. Possible values are TRUE (to have the field by default),
* and FALSE.
*/
#define DEFAULT_FROM TRUE
#define ZOMBIE "<zombie>"
void put_syntax();
char *idletime();
char *logintime();
static char rcsid[]="$Id: tmp-junk.c,v 1.1 2002/02/01 22:46:37 csmall Exp $";
void main (argc, argv)
int argc;
char *argv[];
{
int header=TRUE, long_format=TRUE, ignore_user=TRUE,
from_switch=DEFAULT_FROM, show_pid=FALSE, line_length;
int i, j;
struct utmp *utmp_rec;
struct stat stat_rec;
struct passwd *passwd_entry;
uid_t uid;
char username[9], tty[13], rhost[17], login_time[27];
char idle_time[7], what[1024], pid[10];
char out_line[1024], file_name[256];
char search_name[9];
int jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime;
char /*ch,*/ state, comm[1024], *columns_ptr;
FILE *fp;
search_name[0] = '\0';
/*
* Process the command line
*/
if (argc > 1)
{
/*
* Args that start with '-'
*/
for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++)
{
for (j = 1; argv[i][j] != '\0'; j++)
{
switch (argv[i][j])
{
case 'h':
header = FALSE;
break;
case 's':
long_format = FALSE;
break;
case 'u':
ignore_user = FALSE;
break;
case 'd':
show_pid = TRUE;
break;
case 'f':
if (DEFAULT_FROM == TRUE)
from_switch = FALSE;
else
from_switch = TRUE;
break;
default:
fprintf (stderr, "w: unknown option: '%c'\n",
argv[i][j]);
put_syntax ();
break;
}
}
}
/*
* Check for arg not starting with '-' (ie: username)
*/
if (argc > i)
{
strncpy (search_name, argv[i], 8);
search_name[8] = '\0';
i ++;
if (argc > i)
{
fprintf (stderr, "w: syntax error\n");
put_syntax ();
}
}
}
/*
* Check that /proc is actually there, or else we can't
* get all the information.
*/
if (chdir ("/proc"))
{
fprintf (stderr, "w: fatal error: cannot access /proc\n");
perror (strerror(errno));
exit (-1);
}
/*
* Find out our screen width from $COLUMNS
*/
columns_ptr = getenv ("COLUMNS");
if (columns_ptr == NULL)
{
struct winsize window;
/*
* Try getting it directly
*/
if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0))
line_length = window.ws_col;
else
line_length = 80; /* Default length assumed */
}
else
line_length = atoi (columns_ptr);
/*
* Maybe we should check whether there is enough space on
* the lines for the options selected...
*/
if (line_length < 60)
long_format = FALSE;
line_length --;
/*
* Print whatever headers
*/
if (header == TRUE)
{
/*
* uptime: from MKJ's uptime routine,
* found in whattime.c
*/
print_uptime();
/*
* Print relevant header bits
*/
printf ("User tty ");
if (long_format == TRUE)
{
if (from_switch == TRUE)
printf ("From ");
printf (" login@ idle JCPU PCPU ");
if (show_pid == TRUE)
printf (" PID ");
printf ("what\n");
}
else
{
printf (" idle ");
if (show_pid == TRUE)
printf (" PID ");
printf ("what\n");
}
}
/*
* Process user information.
*/
while ((utmp_rec = getutent()))
{
/*
* Check we actually want to see this record.
* It must be a valid active user process,
* and match a specified search name.
*/
if ( (utmp_rec->ut_type == USER_PROCESS)
&& (strcmp(utmp_rec->ut_user, ""))
&& ( (search_name[0] == '\0')
|| ( (search_name[0] != '\0')
&& !strncmp(search_name, utmp_rec->ut_user, 8) ) ) )
{
/*
* Get the username
*/
strncpy (username, utmp_rec->ut_user, 8);
username[8] = '\0'; /* Set end terminator */
/*
* Find out the uid of that user (from their
* passwd entry)
*/
uid = -1;
if ((passwd_entry = getpwnam (username)) != NULL)
{
uid = passwd_entry->pw_uid;
}
/*
* Get (and clean up) the tty line
*/
for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < 6); i ++)
tty[i] = utmp_rec->ut_line[i];
utmp_rec->ut_line[i] = '\0';
tty[i] = '\0';
/*
* Don't bother getting info if it's not asked for
*/
if (long_format == TRUE)
{
/*
* Get the remote hostname; this can be up to 16 chars,
* but if any chars are invalid (ie: [^a-zA-Z0-9\.])
* then the char is changed to a string terminator.
*/
if (from_switch == TRUE)
{
strncpy (rhost, utmp_rec->ut_host, 16);
rhost[16] = '\0';
}
/*
* Get the login time
* (Calculated by LG's routine, below)
*/
strcpy (login_time, logintime(utmp_rec->ut_time));
}
/*
* Get the idle time.
* (Calculated by LG's routine, below)
*/
strcpy (idle_time, idletime (tty));
/*
* That's all the info out of /etc/utmp.
* The rest is more difficult. We use the pid from
* utmp_rec->ut_pid to look in /proc for the info.
* NOTE: This is not necessarily the active pid, so we chase
* down the path of parent -> child pids until we find it,
* according to the information given in /proc/<pid>/stat.
*/
sprintf (pid, "%d", utmp_rec->ut_pid);
what[0] = '\0';
strcpy (file_name, pid);
strcat (file_name, "/stat");
jcpu = 0;
pcpu = 0;
if ((fp = fopen(file_name, "r")))
{
while (what[0] == '\0')
{
/*
* Check /proc/<pid>/stat to see if the process
* controlling the tty is the current one
*/
fscanf (fp, "%d %s %c %*d %*d %*d %*d %d "
"%*u %*u %*u %*u %*u %d %d %d %d",
&curr_pid, comm, &state, &tpgid,
&utime, &stime, &cutime, &cstime);
fclose (fp);
if (comm[0] == '\0')
strcpy (comm, "-");
/*
* Calculate jcpu and pcpu.
* JCPU is the time used by all processes and their
* children, attached to the tty.
* PCPU is the time used by the current process
* (calculated once after the loop, using last
* obtained values).
*/
if (!jcpu)
jcpu = cutime + cstime;
/*
* Check for a zombie first...
*/
if (state == 'Z')
strcpy (what, ZOMBIE);
else if (curr_pid == tpgid)
{
/*
* If it is the current process, read cmdline
* If that's empty, then the process is swapped out,
* or is a zombie, so we use the command given in stat
* which is in normal round brackets, ie: "()".
*/
strcpy (file_name, pid);
strcat (file_name, "/cmdline");
if ((fp = fopen(file_name, "r")))
{
i = 0;
j = fgetc (fp);
while ((j != EOF) && (i < 256))
{
if (j == '\0')
j = ' ';
what[i] = j;
i++;
j = fgetc (fp);
}
what[i] = '\0';
fclose (fp);
}
if (what[0] == '\0')
strcpy (what, comm);
}
else
{
/*
* Check out the next process
* If we can't open it, use info from this process,
* so we have to check out cmdline first.
*
* If we're not using "-u" then should we just
* say "-" (or "-su") instead of a command line ?
* If so, we should strpcy(what, "-"); when we
* fclose() in the if after the stat() below.
*/
strcpy (file_name, pid);
strcat (file_name, "/cmdline");
if ((fp = fopen (file_name, "r")))
{
i = 0;
j = fgetc (fp);
while ((j != EOF) && (i < 256))
{
if (j == '\0')
j = ' ';
what[i] = j;
i++;
j = fgetc (fp);
}
what[i] = '\0';
fclose (fp);
}
if (what[0] == '\0')
strcpy (what, comm);
/*
* Now we have something in the what variable,
* in case we can't open the next process.
*/
sprintf (pid, "%d", tpgid);
strcpy (file_name, pid);
strcat (file_name, "/stat");
fp = fopen (file_name, "r");
if (fp && (ignore_user == FALSE))
{
/*
* We don't necessarily go onto the next process,
* unless we are either ignoring who the effective
* user is, or it's the same uid
*/
stat (file_name, &stat_rec);
/*
* If the next process is not owned by this
* user finish the loop.
*/
if (stat_rec.st_uid != uid)
{
fclose (fp);
strcpy (what, "-su");
/*
* See comment above somewhere; I've used
* "-su" here, as the next process is owned
* by someone else; this is generally
* because the user has done an "su" which
* then exec'd something else.
*/
}
else
what[0] = '\0';
}
else if (fp) /* else we are ignoring uid's */
what[0] = '\0';
}
}
}
else /* Could not open first process for user */
strcpy (what, "?");
/*
* There is a bug somewhere in my version of linux
* which means that utmp records are not cleaned
* up properly when users log out. However, we
* can detect this, by the users first process
* not being there when we look in /proc.
*/
/*
* Don't output a line for "dead" users.
* This gets round a bug which doesn't update utmp/wtmp
* when users log out.
*/
if (what[0] != '?')
{
#ifdef 0
/* This makes unix98 pty's not line up, so has been disabled - JEH. */
/*
* Remove the letters 'tty' from the tty id
*/
if (!strncmp (tty, "tty", 3))
{
for (i = 3; tty[i - 1] != '\0'; i ++)
tty[i - 3] = tty[i];
}
#endif
/*
* Common fields
*/
sprintf (out_line, "%-9.8s%-6.7s ", username, tty);
/*
* Format the line for output
*/
if (long_format == TRUE)
{
/*
* Calculate CPU usage
*/
pcpu = utime + stime;
jcpu /= 100;
pcpu /= 100;
if (from_switch == TRUE)
sprintf (out_line, "%s %-16.15s", out_line, rhost);
sprintf (out_line, "%s%8.8s ", out_line, login_time);
}
sprintf (out_line, "%s%6s", out_line, idle_time);
if (long_format == TRUE)
{
if (!jcpu)
strcat (out_line, " ");
else if (jcpu/60)
sprintf (out_line, "%s%3d:%02d", out_line,
jcpu/60, jcpu%60);
else
sprintf (out_line, "%s %2d", out_line, jcpu);
if (!pcpu)
strcat (out_line, " ");
else if (pcpu/60)
sprintf (out_line, "%s%3d:%02d", out_line,
pcpu/60, pcpu%60);
else
sprintf (out_line, "%s %2d", out_line, pcpu);
}
if (show_pid == TRUE)
sprintf (out_line, "%s %5.5s", out_line, pid);
strcat (out_line, " ");
strcat (out_line, what);
/*
* Try not to exceed the line length
*/
out_line[line_length] = '\0';
printf ("%s\n", out_line);
}
}
}
}
/*
* put_syntax()
*
* Routine to print the correct syntax to call this program,
* and then exit out appropriately
*/
void put_syntax ()
{
fprintf (stderr, "usage: w [-hfsud] [user]\n");
exit (-1);
}
/*
* idletime()
*
* Routine which returns a string containing
* the idle time of a given user.
*
* This routine was lifted from the original w program
* by Larry Greenfield (greenfie@gauss.rutgers.edu)
* Copyright (c) 1993 Larry Greenfield
*
*/
char *idletime (tty)
char *tty;
{
struct stat terminfo;
unsigned long idle;
char ttytmp[40];
static char give[20];
time_t curtime;
curtime = time (NULL);
sprintf (ttytmp, "/dev/%s", tty);
stat (ttytmp, &terminfo);
idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime;
if (idle >= (60 * 60)) /* more than an hour */
{
if (idle >= (60 * 60 * 48)) /* more than two days */
sprintf (give, "%2ludays", idle / (60 * 60 * 24));
else
sprintf (give, " %2lu:%02u", idle / (60 * 60),
(unsigned) ((idle / 60) % 60));
}
else
{
if (idle / 60)
sprintf (give, "%6lu", idle / 60);
else
give[0]=0;
}
return give;
}
/*
* logintime()
*
* Returns the time given in a suitable format
*
* This routine was lifted from the original w program
* by Larry Greenfield (greenfie@gauss.rutgers.edu)
* Copyright (c) 1993 Larry Greenfield
*
*/
#undef ut_time
char *logintime(ut_time)
time_t ut_time;
{
time_t curtime;
struct tm *logintime, *curtm;
int hour, am, curday, logday;
static char give[20];
static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat" };
static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec" };
curtime = time(NULL);
curtm = localtime(&curtime);
curday = curtm->tm_yday;
logintime = localtime(&ut_time);
hour = logintime->tm_hour;
logday = logintime->tm_yday;
am = (hour < 12);
if (!am)
hour -= 12;
if (hour == 0)
hour = 12;
/*
* This is a newer behavior: it waits 12 hours and the next day, and then
* goes to the 2nd time format. This should reduce confusion.
* It then waits only 6 days (not till the last moment) to go the last
* time format.
*/
if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday))
{
if (curtime > (ut_time + (60 * 60 * 24 * 6)))
sprintf(give, "%2d%3s%2d", logintime->tm_mday,
month[logintime->tm_mon], (logintime->tm_year % 100));
else
sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday],
hour, am ? "am" : "pm");
}
else
sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm");
return give;
}

456
top.1 Normal file
View File

@ -0,0 +1,456 @@
.\" This file Copyright 1992 Robert J. Nation
.\" (nation@rocket.sanders.lockheed.com)
.\" It may be distributed under the GNU Public License, version 2, or
.\" any higher version. See section COPYING of the GNU Public license
.\" for conditions under which this file may be redistributed.
.\"
.\" Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
.\" Cleaned up, and my changes documented. New `.It' macro. Edited.
.\" modified 1996/01/27 Helmut Geyer to match my changes.
.
.de It
.TP 0.5i
.B "\\$1 "
..
.TH TOP 1 "Feb 1 1993" "Linux" "Linux User's Manual"
.SH NAME
top \- display top CPU processes
.SH SYNOPSIS
.B top
.RB [ \- ]
.RB [ d
.IR delay ]
.RB [ p
.IR pid ]
.RB [ q ]
.RB [ c ]
.RB [ C ]
.RB [ S ]
.RB [ s ]
.RB [ i ]
.RB [ n
.IR iter ]
.RB [ b ]
.SH DESCRIPTION
.B top
provides an ongoing look at processor activity in real time. It
displays a listing of the most CPU-intensive tasks on the system, and
can provide an interactive interface for manipulating processes.
It can sort the tasks by CPU usage, memory usage and runtime.
.N top
can be better configured than the standard top from the procps suite.
Most features can either be selected by an interactive command or by
specifying the feature in the personal or system-wide configuration
file. See below for more information.
.PP
.SH "COMMAND\-LINE OPTIONS"
.It d
Specifies the delay between screen updates. You can change this with
the
.B s
interactive command.
.It p
Monitor only processes with given process id.
This flag can be given up to twenty times. This option is neither
available interactively nor can it be put into the configuration file.
.It q
This causes
.B top
to refresh without any delay. If the caller has superuser priviledges,
top runs with the highest possible priority.
.It S
Specifies cumulative mode, where each process is listed with the CPU
time that it
.I as well as its dead children
has spent. This is like the
.B -S
flag to
.BR ps (1).
See the discussion below of the
.B S
interactive command.
.It s
Tells
.B top
to run in secure mode. This disables the potentially dangerous of the
interactive commands (see below). A secure
.B top
is a nifty thing to leave running on a spare terminal.
.It i
Start
.B top
ignoring any idle or zombie processes. See the interactive command
.B i
below.
.It C
display total CPU states in addition to individual CPUs. This option
only affects SMP systems.
.It c
display command line instead of the command name only. The default
behaviour has been changed as this seems to be more useful.
.It n
Number of iterations. Update the display this number of times and then exit.
.It b
Batch mode. Useful for sending output from top to other programs or to a file.
In this mode,
.B top
will not accept command line input. It runs until it produces the number of
iterations requested with the
.B n
option or until killed. Output is plain text suitable for display on a dumb
terminal.
.
.SH "FIELD DESCRIPTIONS"
.B top
displays a variety of information about the processor state. The
display is updated every 5 seconds by default, but you can change that
with the
.B d
command-line option or the
.B s
interactive command.
.It "uptime"
This line displays the time the system has been up, and the three load
averages for the system. The load averages are the average number of
process ready to run during the last 1, 5 and 15 minutes. This line is
just like the output of
.BR uptime (1).
The uptime display may be toggled by the interactive
.B l
command.
.It processes
The total number of processes running at the time of the last update.
This is also broken down into the number of tasks which are running,
sleeping, stopped, or undead. The processes and states display may be
toggled by the
.B t
interactive command.
.It "CPU states"
Shows the percentage of CPU time in user mode, system mode, niced tasks,
and idle. (Niced tasks are only those whose nice value is negative.)
Time spent in niced tasks will also be counted in system and user time,
so the total will be more than 100%. The processes and states display
may be
toggled by the
.B t
interactive command.
.It Mem
Statistics on memory usage, including total available memory, free
memory, used memory, shared memory, and memory used for buffers. The
display of memory information may be toggled by the
.B m
interactive command.
.It Swap
Statistics on swap space, including total swap space, available swap
space, and used swap space. This and
.B Mem
are just like the output of
.BR free (1).
.It PID
The process ID of each task.
.It PPID
The parent process ID each task.
.It UID
The user ID of the task's owner.
.It USER
The user name of the task's owner.
.It PRI
The priority of the task.
.It NI
The nice value of the task. Negative nice values are higher priority.
.It SIZE
The size of the task's code plus data plus stack space, in kilobytes,
is shown here.
.It TSIZE
The code size of the task. This gives strange values for kernel
processes and is broken for ELF processes.
.It DSIZE
Data + Stack size. This is broken for ELF processes.
.It TRS
Text resident size.
.It SWAP
Size of the swapped out part of the task.
.It D
Size of pages marked dirty.
.It LC
Last used processor. (That this changes from time to time is not
a bug; Linux intentionally uses weak affinity. Also notice that
the very act of running top may break weak affinity and cause more
processes to change current CPU more often because of the extra
demand for CPU time.)
.It RSS
The total amount of physical memory used by the task, in kilobytes, is
shown here. For ELF processes used library pages are counted here, for
a.out processes not.
.It SHARE
The amount of shared memory used by the task is shown in this column.
.It STAT
The state of the task is shown here. The state is either
.B S
for sleeping,
.B D
for uninterruptible sleep,
.B R
for running,
.B Z
for zombies, or
.B T
for stopped or traced. These states are modified by trailing
.B <
for a process with negative nice value,
.B N
for a process with positive nice value,
.B W
for a swapped out process (this does not work correctly for kernel
processes).
.It WCHAN
depending on the availablity of either
.I /boot/psdatabase
or the kernel link map
.I /boot/System.map
this shows the address or the name of the kernel function the task
currently is sleeping in.
.It TIME
Total CPU time the task has used since it started. If cumulative mode
is on, this also includes the CPU time used by the process's children
which have died. You can set cumulative mode with the
.B S
command line option or toggle it with the interactive command
.BR S .
The header line will then be changed to
.BR CTIME .
.It %CPU
The task's share of the CPU time since the last screen update, expressed
as a percentage of total CPU time per processor.
.It %MEM
The task's share of the physical memory.
.It COMMAND
The task's command name, which will be truncated if it is too long to be
displayed on one line. Tasks in memory will have a full command line,
but swapped-out tasks will only have the name of the program in
parentheses (for example, "(getty)").
.It "A , WP"
these fields from the kmem top are not supported.
.
.SH "INTERACTIVE COMMANDS"
Several single-key commands are recognized while
.B top
is running. Some are disabled if the
.B s
option has been given on the command line.
.It space
Immediately updates the display.
.It ^L
Erases and redraws the screen.
.It "h\fR or \fB?"
Displays a help screen giving a brief summary of commands, and the
status of secure and cumulative modes.
.It k
Kill a process. You will be prompted for the PID of the task, and the
signal to send to it. For a normal kill, send signal 15. For a sure,
but rather abrupt, kill, send signal 9. The default signal, as with
.BR kill (1),
is 15,
.BR SIGTERM .
This command is not available in secure mode.
.It i
Ignore idle and zombie processes. This is a toggle switch.
.It I
Toggle between Solaris (CPU percentage divided by total number of CPUs)
and Irix (CPU percentage calculated solely by amount of time) views.
This is a toggle switch that affects only SMP systems.
.It "n\fR or \fB#"
Change the number of processes to show. You will be prompted to enter
the number. This overrides automatic determination of the number of
processes to show, which is based on window size measurement. If 0 is
specified, then top will show as many processes as will fit on the
screen; this is the default.
.It q
Quit.
.It r
Re-nice a process. You will be prompted for the PID of the task, and
the value to nice it to. Entering a positve value will cause a process
to be niced to negative values, and lose priority. If root is running
.BR top ,
a negative value can be entered, causing a process to get a higher than
normal priority. The default renice value is 10. This command is not
available in secure mode.
.It S
This toggles cumulative mode, the equivalent of
.BR "ps -S" ,
i.e., that CPU times will include a process's defunct children. For
some programs, such as compilers, which work by forking into many
seperate tasks, normal mode will make them appear less demanding than
they actually are. For others, however, such as shells and
.BR init ,
this behavior is correct. In any case, try cumulative mode for an
alternative view of CPU use.
.It s
Change the delay between updates. You will be prompted to enter the
delay time, in seconds, between updates. Fractional values are
recognized down to microseconds. Entering 0 causes continuous updates.
The default value is 5 seconds. Note that low values cause nearly
unreadably fast displays, and greatly raise the load. This command is
not available in secure mode.
.It "f\fR or \fBF"
Add fields to display or remove fields from the display. See below for
more information.
.It "o\fR or \fBO"
Change order of displayed fields. See below for more information.
.It l
toggle display of load average and uptime information.
.It m
toggle display of memory information.
.It t
toggle display of processes and CPU states information.
.It c
toggle display of command name or full command line.
.It N
sort tasks by pid (\fIn\fPumerically).
.It A
sort tasks by age (newest first).
.It P
sort tasks by CPU usage (default).
.It M
sort tasks by resident memory usage.
.It T
sort tasks by time / cumulative time.
.It W
Write current setup to
.IR ~/.toprc .
This is the recommended way to write a top configuration file.
.
.SH The Field and Order Screens
After pressing
.BR f ,
.BR F ,
.B o
or
.B O
you will be shown a screen specifying the field order on the top line
and short descriptions of the field contents. The field order string
uses the following syntax: If the letter in the filed string
corresponding to a field is upper case, the field will be displayed.
This is furthermore indicated by an asterisk in front of the field
description.
The order of the fields corresponds to the order of the letters in the
string.
From the field select screen you can toggle the display of a field by
pressing the corresponding letter.
From the order screen you may move a field to the left by pressing
the corresponding upper case letter resp. to the right by pressing the
lower case one.
.
.SH Configuration Files
Top reads it's default configuration from two files,
.I /etc/toprc
and
.IR ~/.toprc .
The global configuration file may be used to restrict the usage of top
to the secure mode for non-priviledged users. If this is desired, the
file should contain a 's' to specify secure mode and a digit d (2<=d<=9)
for the default delay (in seconds) on a single line.
.
The personal configuration file contains two lines. The first line
contains lower and upper letters to specify which fields in what
order are to be displayed. The letters correspond to the letters in the
Fields or Order screens from top. As this is not very instructive, it is
recommended to select fields and order in a running top process and to
save this using the
.I W
interactive command.
.
The second line is more interesting (and important). It contains
information on the other options. Most important, if you have saved a
configuration in secure mode, you will not get an insecure top without
removing the lower 's' from the second line of your
.IR ~/.toprc .
A digit specifies the delay time between updates, a capital 'S'
cumulative mode, a lower 'i' no-idle mode, a capital 'I' Irix view. As
in interactive mode, a lower 'm', 'l', and 't' suppresses the display
of memory, uptime resp. process and CPU state information.
Currently changing the default sorting order (by CPU usage) is not
supported.
.
.SH NOTES
This
.BR proc -based
.B top
works by reading the files in the
.B proc
filesystem,
mounted on
.IR /proc .
If
.I /proc
is not mounted,
.B top
will not work.
.PP
.B %CPU
shows the cputime/realtime percentage in the period of time between
updates. For the first update, a short delay is used, and
.B top
itself dominates the CPU usage. After that,
.B top
will drop back, and a more reliable estimate of CPU usage is available.
.PP
The
.B SIZE
and
.B RSS fields don't count the page tables and the
.B task_struct
of a process; this is at least 12K of memory that is always resident.
.B SIZE
is the virtual size of the process (code+data+stack).
.PP
Keep in mind that a process must die for its time to be recorded on its
parent by cumulative mode. Perhaps more useful behavior would be to
follow each process upwards, adding time, but that would be more
expensive, possibly prohibitively so. In any case, that would make
.BR top 's
behavior incompatible with
.BR ps .
.
.SH FILES
.I /etc/toprc
The global configuration file.
.I ~/.toprc
The personal configuration file.
.
.SH "SEE ALSO"
.BR ps (1),
.BR free (1),
.BR uptime (1),
.BR kill (1),
.BR renice (1).
.
.SH
BUGS
If the window is less than about 70x7,
.B top
will not format information correctly.
Many fields still have problems with ELF processes.
the help screens are not yet optimized for windows with less than
25 lines
.
.SH AUTHOR
.B top
was originally written by Roger Binns, based on Branko Lankester's
<lankeste@fwi.uva.nl> ps program.
Robert Nation <nation@rocket.sanders.lockheed.com> re-wrote it
significantly to use the proc filesystem, based on Michael K. Johnson's
<johnsonm@redhat.com> proc-based ps program.
Michael Shields <mjshield@nyx.cs.du.edu> made many changes, including
secure and cumulative modes and a general cleanup.
Tim Janik <timj@gtk.org> added age sorting and the ability to monitor
specific processes through their ids.
Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
Heavily changed it to include support for configurable fields and other
new options, and did further cleanup and use of the new readproc interface.
The "b" and "n" options contributed by George Bonser <george@captech.com>
for CapTech IT Services.
Please send bug reports to <acahalan@cs.uml.edu>

1746
top.c Normal file

File diff suppressed because it is too large Load Diff

6
top.desktop Normal file
View File

@ -0,0 +1,6 @@
[Desktop Entry]
Name=Top
Type=Application
Comment=Repeatedly display processes by CPU time, memory usage, etc.
Exec=top
Terminal=true

228
top.h Normal file
View File

@ -0,0 +1,228 @@
/*
* top.h header file 1996/05/18,
*
* function prototypes, global data definitions and string constants.
*/
static proc_t** readproctab2(int flags, proc_t** tab, ...);
static void parse_options(char *Options, int secure);
static void get_options(void);
static void error_end(int rno);
static void end(int signo);
static void stop(int signo);
static void window_size(int signo);
static int make_header(void);
static char *getstr(void);
static int getsig(void);
static float getfloat(void);
static int time_sort(proc_t **P, proc_t **Q);
static int pcpu_sort(proc_t **P, proc_t **Q);
static int mem_sort(proc_t **P, proc_t **Q);
static int age_sort(proc_t **P, proc_t **Q);
static void show_fields(void);
static void change_order(void);
static void change_fields(void);
static void show_task_info(proc_t *task);
static void show_procs(void);
static float get_elapsed_time(void);
static void show_meminfo(void);
static void do_stats(proc_t** p, float elapsed_time, int pass);
static void do_key(char c);
/* configurable field display support */
static int pflags[30];
static int Numfields;
/* Name of the config file (in $HOME) */
#ifndef RCFILE
#define RCFILE ".toprc"
#endif
#ifndef SYS_TOPRC
#define SYS_TOPRC "/etc/toprc"
#endif
#define MAXLINES 2048
#define MAXNAMELEN 1024
/* this is what procps top does by default, so let's do this, if nothing is
* specified
*/
#ifndef DEFAULT_SHOW
/* 0 1 2 3 */
/* 0123456789012345678901234567890 */
#define DEFAULT_SHOW "AbcDgHIjklMnoTP|qrsuzyV{EFWX"
#endif
static char Fields[256] = "";
/* This structure stores some critical information from one frame to
the next. mostly used for sorting. Added cumulative and resident fields. */
struct save_hist {
int ticks;
int pid;
int pcpu;
int utime;
int stime;
};
/* The original terminal attributes. */
static struct termios Savetty;
/* The new terminal attributes. */
static struct termios Rawtty;
/* Cached termcap entries. */
static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr;
/* Current window size. Note that it is legal to set Display_procs
larger than can fit; if the window is later resized, all will be ok.
In other words: Display_procs is the specified max number of
processes to display (zero for infinite), and Maxlines is the actual
number. */
static int Lines, Cols, Maxlines, Display_procs;
/* Maximum length to display of the command line of a process. */
static unsigned Maxcmd;
/* Controls how long we sleep between screen updates. Accurate to
microseconds. */
static float Sleeptime = 5;
/* for opening/closing the system map */
static int psdbsucc = 0;
/* Mode flags. */
static int Irixmode = 1;
static int Secure = 0;
static int Cumulative = 0;
static int Noidle = 0;
static int CPU_states = 0;
static char CurrUser[BUFSIZ];
static int CL_pg_shift = (PAGE_SHIFT - 10);
static int CL_wchan_nout = -1;
static int show_stats = 1; /* show status summary */
static int show_memory = 1; /* show memory summary */
static int show_loadav = 1; /* show load average and uptime */
static int show_cmd = 1; /* show command name instead of commandline */
static pid_t monpids[520]; /* randomly chosen value */
static const int monpids_max = sizeof(monpids)/sizeof(pid_t);
static int monpids_index = 0;
static int Loops = -1; /* number of iterations. -1 loops forever */
static int Batch = 0; /* batch mode. Collect no input, dumb output */
/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */
enum {
S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE
};
/* default sorting by CPU% */
static int sort_type = S_PCPU;
/* flags for each possible field. At the moment up to 30 are supported */
enum {
P_PID, P_PPID, P_EUID, P_EUSER,
P_PCPU, P_PMEM, P_TTY, P_PRI,
P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ,
P_SIZE, P_TRS, P_SWAP, P_SHARE,
P_A, P_WP, P_DT, P_RSS,
P_WCHAN, P_STAT, P_TIME, P_COMMAND,
P_LCPU, P_FLAGS, P_END
};
/* corresponding headers */
static char *headers[] =
{
" PID ", " PPID ", " UID ",
"USER ", "%CPU ", "%MEM ",
"TTY ", "PRI ", " NI ",
"PAGEIN ", "TSIZE ", "DSIZE ",
" SIZE ", " TRS ", "SWAP ",
"SHARE ", " A ", " WP ",
" D ", " RSS ", "WCHAN ",
"STAT ", " TIME ", "COMMAND",
"LC ",
" FLAGS "
};
/* corresponding field desciptions */
static char *headers2[] =
{
"Process Id", "Parent Process Id", "User Id",
"User Name", "CPU Usage", "Memory Usage",
"Controlling tty", "Priority", "Nice Value",
"Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)",
"Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb",
"Shared Pages (kb)", "Accessed Page count", "Write Protected Pages",
"Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function",
"Process Status", "CPU Time", "Command",
"Last used CPU (expect this to change regularly)",
"Task Flags (see linux/sched.h)"
};
/* The header printed at the top of the process list.*/
static char Header[MAXLINES];
/* The response to the interactive 'h' command. */
#define HELP_SCREEN "\
Interactive commands are:\n\
\n\
space\tUpdate display\n\
^L\tRedraw the screen\n\
fF\tadd and remove fields\n\
oO\tChange order of displayed fields\n\
h or ?\tPrint this list\n\
S\tToggle cumulative mode\n\
i\tToggle display of idle proceses\n\
I\tToggle between Irix and Solaris views (SMP-only)\n\
c\tToggle display of command name/line\n\
l\tToggle display of load average\n\
m\tToggle display of memory information\n\
t\tToggle display of summary information\n\
k\tKill a task (with any signal)\n\
r\tRenice a task\n\
N\tSort by pid (Numerically)\n\
A\tSort by age\n\
P\tSort by CPU usage\n\
M\tSort by resident memory usage\n\
T\tSort by time / cumulative time\n\
u\tShow only a specific user\n\
n or #\tSet the number of process to show\n\
s\tSet the delay in seconds between updates\n\
W\tWrite configuration file ~/.toprc\n\
q\tQuit"
#define SECURE_HELP_SCREEN "\
Interactive commands available in secure mode are:\n\
\n\
space\tUpdate display\n\
^L\tRedraw the screen\n\
fF\tadd and remove fields\n\
h or ?\tPrint this list\n\
S\tToggle cumulative mode\n\
i\tToggle display of idle proceses\n\
c\tToggle display of command name/line\n\
l\tToggle display of load average\n\
m\tToggle display of memory information\n\
t\tToggle display of summary information\n\
n or #\tSet the number of process to show\n\
u\tShow only a specific user\n\
oO\tChange order of displayed fields\n\
W\tWrite configuration file ~/.toprc\n\
q\tQuit"
/* Number of lines needed to display the header information. */
static int header_lines;
/* ############## Some Macro definitions for screen handling ######### */
/* String to use in error messages. */
#define PROGNAME "top"
/* Clear the screen. */
#define clear_screen() \
printf("%s", cl)
/* Show an error in the context of the spiffy full-screen display. */
#define SHOWMESSAGE(x) do { \
printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr); \
printf x; \
printf ("%s",me); \
fflush(stdout); \
sleep(2); \
} while (0)

34
uptime.1 Normal file
View File

@ -0,0 +1,34 @@
.\" -*-Nroff-*-
.\"
.TH UPTIME 1 "26 Jan 1993" "Cohesive Systems" "Linux User's Manual"
.SH NAME
uptime \- Tell how long the system has been running.
.SH SYNOPSIS
.B uptime
.br
.BR uptime " [" "\-V" ]
.SH DESCRIPTION
.B uptime
gives a one line display of the following information.
The current time,
how long the system has been running,
how many users are currently logged on,
and the system load averages for the past 1, 5, and 15 minutes.
.sp
This is the same information contained in the header line displayed by
.BR w (1).
.SH FILES
.IR /var/run/utmp " information about who is currently logged on"
.br
.IR /proc " process information"
.SH AUTHORS
.B uptime
was written by Larry Greenfield <greenfie@gauss.rutgers.edu> and
Michael K. Johnson <johnsonm@sunsite.unc.edu>.
Please send bug reports to <acahalan@cs.uml.edu>
.SH "SEE ALSO"
.BR ps (1),
.BR top (1),
.BR utmp (5),
.BR w (1)

9
uptime.c Normal file
View File

@ -0,0 +1,9 @@
#include <string.h>
#include "proc/whattime.h"
#include "proc/version.h"
int main(int argc, char *argv[]) {
if(argc == 1) print_uptime();
if((argc == 2) && (!strcmp(argv[1], "-V"))) display_version();
return 0;
}

178
utmp.c Normal file
View File

@ -0,0 +1,178 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <time.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
/* examine and fix utmp entries. Note that the code for fixing entries
is not complete, and indeed does nothing at all at this time. No bug
reports, please, as I am still actively working on this. It is not
here for general use, but only so that I can ferret out any bugs that
exist on other peoples systems without having to log in to their systems
myself ;-) */
int main (int argc, char **argv) {
FILE *ut; /* /var/run/utmp */
struct utmp uts; /* utmp record */
char user[UT_NAMESIZE + 1];
char host[17];
char ch;
int print_all = 0, list = 0, fix = 0;
/* get options */
while ((ch = getopt(argc, argv, "laf")) != EOF)
switch (ch) {
case 'a':
print_all = 1;
break;
case 'l':
list = 1;
break;
case 'f':
fix = 1;
break;
}
/* check argument options */
if ( (!list && !print_all && !fix)) {
fprintf(stderr, "You must specify a command line option:\n\tl = list\n\
\tf = fix\n\ta = all (requires l or f)\n");
exit(1);
}
if (list) {
ut = fopen(UTMP_FILE, "r");
while (fread(&uts, sizeof(uts), 1, ut))
if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000'))
|| print_all) {
strncpy(user, uts.ut_user, UT_NAMESIZE);
user[UT_NAMESIZE]=0;
strncpy(host, uts.ut_host, 16);
host[16]=0;
printf("ut_type: %d\n", uts.ut_type);
printf("ut_pid: %d\n", uts.ut_pid);
printf("ut_line: %s\n", uts.ut_line);
printf("ut_id: %2s\n", uts.ut_id);
printf("ut_time: %d\n", uts.ut_time);
printf("ut_user: %s\n", user);
printf("ut_host: %s\n", host);
printf("ut_addr: %d\n\n", uts.ut_addr);
}
fclose(ut);
}
if (fix) {
ut = fopen(UTMP_FILE, "r");
while (fread(&uts, sizeof(uts), 1, ut))
if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000'))
|| print_all) {
/* Display entry in utmp */
strncpy(user, uts.ut_user, UT_NAMESIZE);
user[UT_NAMESIZE]=0;
strncpy(host, uts.ut_host, 16);
host[16]=0;
printf("ut_type: %d\n", uts.ut_type);
printf("ut_pid: %d\n", uts.ut_pid);
printf("ut_line: %s\n", uts.ut_line);
printf("ut_id: %s2\n", uts.ut_id);
printf("ut_time: %d\n", uts.ut_time);
printf("ut_user: %s\n", user);
printf("ut_host: %s\n", host);
printf("ut_addr: %d\n\n", uts.ut_addr);
printf("Modify this record? (y/N): "); fflush(stdout);
/* Ask if to delete or no */
if ((ch = getchar()) == 'y' || ch == 'Y') {
while (getchar() != '\n');
printf("Change ut_type? "); fflush(stdout);
if ((ch = getchar()) == 'y' || ch == 'Y') {
while (getchar() != '\n');
printf("INIT, LOGIN, USER, or DEAD_PROCESS? (I/L/U/D): ");
fflush(stdout);
ch = getchar();
switch (ch) {
case 'i':
case 'I':
uts.ut_type = INIT_PROCESS;
break;
case 'l':
case 'L':
uts.ut_type = LOGIN_PROCESS;
break;
case 'u':
case 'U':
uts.ut_type = USER_PROCESS;
break;
case 'd':
case 'D':
uts.ut_type = DEAD_PROCESS;
break;
default:
printf("Invalid choice: %c\n", ch);
}
if (ch != '\n') while ((ch = getchar()) != '\n');
}
if (ch != '\n') while ((ch = getchar()) != '\n');
printf("Change ut_id field? (y/N): "); fflush(stdout);
if ((ch = getchar()) == 'y' || ch == 'Y') {
while (getchar() != '\n');
printf("Please enter the two characters for ut_id: ");
fflush(stdout);
uts.ut_id[0] = getchar();
uts.ut_id[1] = getchar();
while ((ch = getchar()) != '\n');
}
if (ch != '\n') while ((ch = getchar()) != '\n');
printf("Change the ut_user field? (y/N): "); fflush(stdout);
if ((ch = getchar()) == 'y' || ch == 'Y') {
int i;
while (getchar() != '\n');
printf("Please enter the new ut_name, up to %c characters: ",
UT_NAMESIZE);
fflush(stdout);
for (i=0; i<UT_NAMESIZE; i++) {
ch = getchar();
uts.ut_user[i] = (ch != '\n') ? ch : i = UT_NAMESIZE, (char) 0;
}
}
if (ch != '\n') while ((ch = getchar()) != '\n');
printf("Change the ut_host field? (y/N): "); fflush(stdout);
if ((ch = getchar()) == 'y' || ch == 'Y') {
int i;
while (getchar() != '\n');
printf("Please enter the new ut_host, up to 16 characters: ");
fflush(stdout);
for (i=0; i<16; i++) {
ch = getchar();
uts.ut_user[i] = (ch != '\n') ? ch : i = 16, (char) 0;
}
if (ch != '\n') while ((ch = getchar()) != '\n');
}
/* Here go the changes...*/
/* utmpname(UTMP_FILE);
setutent();
pututline(&uts);
endutent(); */
/* But they don't work... */
}
if (ch != '\n') while ((ch = getchar()) != '\n');
/* here we should write the utmp entry */
}
fclose(ut);
}
return 0;
}

105
vmstat.8 Normal file
View File

@ -0,0 +1,105 @@
.\" 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 [ "\-n" ]
.RI [ delay " [ " count ]]
.br
.BR vmstat [ "\-V" ]
.SH DESCRIPTION
\fBvmstat\fP reports information about processes, memory, paging,
block IO, traps, and cpu activity.
The first report produced gives averages since the last reboot. Additional
reports give information on a sampling period of length \fIdelay\fP.
The process and memory reports are instantaneous in either case.
.SS Options
The \fB-n\fP switch causes the header to be displayed only once rather than periodically.
.PP
.I delay
is the delay between updates in seconds. If no delay is specified,
only one report is printed with the average values since boot.
.PP
.I count
is the number of updates. If no count is specified and delay is
defined, \fIcount\fP defaults to infinity.
.PP
The \fB-V\fP switch results in displaying version information.
.PP
.SH FIELD DESCRIPTIONS
.SS
.B "Procs"
.nf
r: The number of processes waiting for run time.
b: The number of processes in uninterruptable sleep.
w: The number of processes swapped out but otherwise runnable. This
field is calculated, but Linux never desperation swaps.
.fi
.PP
.SS
.B "Memory"
.nf
swpd: the amount of virtual memory used (kB).
free: the amount of idle memory (kB).
buff: the amount of memory used as buffers (kB).
.fi
.PP
.SS
.B "Swap"
.nf
si: Amount of memory swapped in from disk (kB/s).
so: Amount of memory swapped to disk (kB/s).
.fi
.PP
.SS
.B "IO"
.nf
bi: Blocks sent to a block device (blocks/s).
bo: Blocks received from a block device (blocks/s).
.fi
.PP
.SS
.B "System"
.nf
in: The number of interrupts per second, including the clock.
cs: The number of context switches per second.
.if
.PP
.SS
.B "CPU "
These are percentages of total CPU time.
.nf
us: user time
sy: system time
id: idle time
.nf
.SH NOTES
.B "vmstat "
does not require special permissions.
.PP
These reports are intended to help identify system bottlenecks. Linux
.B "vmstat "
does not count itself as a running process.
.PP
All linux blocks are currently 1k, except for CD-ROM blocks which are 2k.
.PP
.SH FILES
.ta
.nf
/proc/meminfo
/proc/stat
/proc/*/stat
.fi
.SH "SEE ALSO"
ps(1), top(1), free(1)
.PP
.SH BUGS
Does not tabulate the block io per device or count the number of system calls.
.SH AUTHOR
Written by Henry Ware <al172@yfn.ysu.edu>.

278
vmstat.c Normal file
View File

@ -0,0 +1,278 @@
#define VERSION Version: 0.99, last modified 15 January 94: ALPHA
#define PROGNAME "vmstat"
/* Copyright 1994 by Henry Ware <al172@yfn.ysu.edu>. Copyleft same year. */
/* This attempts to display virtual memory statistics.
vmstat does not need to be run as root.
*/
/* TODO etc.
* Count the system calls. How? I don't even know where to start.
* add more items- aim at Posix 1003.2 (1003.7?) compliance, if relevant (I.
don't think it is, but let me know.)
* sometimes d(inter)<d(ticks)!! This is not possible: inter is a sum of
positive numbers including ticks. But it happens. This doesn't seem to
affect things much, however.
* It would be interesting to see when the buffers avert a block io:
Like SysV4's "sar -b"... it might fit better here, with Linux's variable
buffer size.
* Ideally, blocks in & out would be counted in 1k increments, rather than
by block: this only makes a difference for CDs and is a problematic fix.
*/
/* PROCPS
This is part of the procps package maintained by Michael K. Johnson
<johnsonm@redhat.com>; report bugs to <acahalan@cs.uml.edu>.
*/
#include "proc/sysinfo.h"
#include "proc/version.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/dir.h>
#include <dirent.h>
#define NDEBUG !DEBUG
#define BUFFSIZE 4096
#define FALSE 0
#define TRUE 1
static char buff[BUFFSIZE]; /* used in the procedures */
typedef unsigned long jiff;
/****************************************************************/
static void usage(void) {
fprintf(stderr,"usage: %s [-V] [-n] [delay [count]]\n",PROGNAME);
fprintf(stderr," -V prints version.\n");
fprintf(stderr," -n causes the headers not to be reprinted regularly.\n");
fprintf(stderr," delay is the delay between updates in seconds. \n");
fprintf(stderr," count is the number of updates.\n");
exit(EXIT_FAILURE);
}
static void crash(char *filename) {
perror(filename);
exit(EXIT_FAILURE);
}
static int winhi(void) {
struct winsize win;
int rows = 24;
if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_row > 0)
rows = win.ws_row;
return rows;
}
static void showheader(void){
printf("%8s%28s%8s%12s%11s%12s\n",
"procs","memory","swap","io","system","cpu");
printf("%2s %2s %2s %6s %6s %6s %6s %3s %3s %5s %5s %4s %5s %3s %3s %3s\n",
"r","b","w","swpd","free","buff","cache","si","so","bi","bo",
"in","cs","us","sy","id");
}
static void getstat(jiff *cuse, jiff *cice, jiff *csys, jiff long *cide,
unsigned *pin, unsigned *pout, unsigned *s_in, unsigned *sout,
unsigned *itot, unsigned *i1, unsigned *ct) {
static int Stat;
if ((Stat=open("/proc/stat", O_RDONLY, 0)) != -1) {
char* b;
buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */
read(Stat,buff,BUFFSIZE-1);
close(Stat);
*itot = 0;
*i1 = 1; /* ensure assert below will fail if the sscanf bombs */
b = strstr(buff, "cpu ");
sscanf(b, "cpu %lu %lu %lu %lu", cuse, cice, csys, cide);
b = strstr(buff, "page ");
sscanf(b, "page %u %u", pin, pout);
b = strstr(buff, "swap ");
sscanf(b, "swap %u %u", s_in, sout);
b = strstr(buff, "intr ");
sscanf(b, "intr %u %u", itot, i1);
b = strstr(buff, "ctxt ");
sscanf(b, "ctxt %u", ct);
}
else {
crash("/proc/stat");
}
}
static void getrunners(unsigned int *running, unsigned int *blocked,
unsigned int *swapped) {
static struct direct *ent;
static char filename[80];
static int fd;
static unsigned size;
static char c;
DIR *proc;
*running=0;
*blocked=0;
*swapped=0;
if ((proc=opendir("/proc"))==NULL) crash("/proc");
while((ent=readdir(proc))) {
if (isdigit(ent->d_name[0])) { /*just to weed out the obvious junk*/
sprintf(filename, "/proc/%s/stat", ent->d_name);
if ((fd = open(filename, O_RDONLY, 0)) != -1) { /*this weeds the rest*/
read(fd,buff,BUFFSIZE-1);
sscanf(buff, "%*d %*s %c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u\n",&c,&size);
close(fd);
if (c=='R') {
if (size>0) (*running)++;
else (*swapped)++;
}
else if (c=='D') {
if (size>0) (*blocked)++;
else (*swapped)++;
}
}
}
}
closedir(proc);
#if 1
/* is this next line a good idea? It removes this thing which
uses (hopefully) little time, from the count of running processes */
(*running)--;
#endif
}
int main(int argc, char *argv[]) {
const char format[]="%2u %2u %2u %6u %6u %6u %6u %3u %3u %5u %5u %4u %5u %3u %3u %3u\n";
unsigned int height=22; /* window height, reset later if needed. */
#if 0
unsigned long int args[2]={0,0};
#endif
unsigned int moreheaders=TRUE;
unsigned int tog=0; /* toggle switch for cleaner code */
unsigned int i,hz;
unsigned int running,blocked,swapped;
jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2];
jiff duse,dsys,didl,Div,divo2;
unsigned int pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
unsigned int inter[2],ticks[2],ctxt[2];
unsigned int per=0, pero2;
unsigned long num=0;
unsigned int kb_per_page = sysconf(_SC_PAGESIZE) / 1024;
setlinebuf(stdout);
argc=0; /* redefined as number of integer arguments */
per=1;
num=0;
for (argv++;*argv;argv++) {
if ('-' ==(**argv)) {
switch (*(++(*argv))) {
case 'V':
display_version();
exit(0);
case 'n':
/* print only one header */
moreheaders=FALSE;
break;
default:
/* no other aguments defined yet. */
usage();
}
} else {
argc++;
switch (argc) {
case 1:
if ((per = atoi(*argv)) == 0)
usage();
num = ULONG_MAX;
break;
case 2:
num = atol(*argv);
break;
default:
usage();
} /* switch */
}
}
if (moreheaders) {
int tmp=winhi()-3;
height=((tmp>0)?tmp:22);
}
pero2=(per/2);
showheader();
getrunners(&running,&blocked,&swapped);
meminfo();
getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl,
pgpgin,pgpgout,pswpin,pswpout,
inter,ticks,ctxt);
duse= *cpu_use + *cpu_nic;
dsys= *cpu_sys;
didl= *cpu_idl;
Div= duse+dsys+didl;
hz=sysconf(_SC_CLK_TCK); /* get ticks/s from system */
divo2= Div/2UL;
printf(format,
running,blocked,swapped,
kb_swap_used,kb_main_free,kb_main_buffers,kb_main_cached,
(*pswpin *kb_per_page*hz+divo2)/Div,
(*pswpout*kb_per_page*hz+divo2)/Div,
(*pgpgin *hz+divo2)/Div,
(*pgpgout *hz+divo2)/Div,
(*inter *hz+divo2)/Div,
(*ctxt *hz+divo2)/Div,
(100*duse+divo2)/Div,
(100*dsys+divo2)/Div,
(100*didl+divo2)/Div
);
for(i=1;i<num;i++) { /* \\\\\\\\\\\\\\\\\\\\ main loop ////////////////// */
sleep(per);
if (moreheaders && ((i%height)==0)) showheader();
tog= !tog;
getrunners(&running,&blocked,&swapped);
meminfo();
getstat(cpu_use+tog,cpu_nic+tog,cpu_sys+tog,cpu_idl+tog,
pgpgin+tog,pgpgout+tog,pswpin+tog,pswpout+tog,
inter+tog,ticks+tog,ctxt+tog);
duse= cpu_use[tog]-cpu_use[!tog] + cpu_nic[tog]-cpu_nic[!tog];
dsys= cpu_sys[tog]-cpu_sys[!tog];
didl= cpu_idl[tog]-cpu_idl[!tog];
/* idle can run backwards for a moment -- kernel "feature" */
if(cpu_idl[tog]<cpu_idl[!tog]) didl=0;
Div= duse+dsys+didl;
divo2= Div/2UL;
printf(format,
running,blocked,swapped,
kb_swap_used,kb_main_free,kb_main_buffers,kb_main_cached,
( (pswpin [tog]-pswpin [!tog])*kb_per_page+pero2 )/per,
( (pswpout[tog]-pswpout[!tog])*kb_per_page+pero2 )/per,
( pgpgin [tog]-pgpgin [!tog] +pero2 )/per,
( pgpgout[tog]-pgpgout[!tog] +pero2 )/per,
(inter[tog]-inter[!tog]+pero2)/per,
(ctxt[tog]-ctxt[!tog]+pero2)/per,
(100*duse+divo2)/Div,
(100*dsys+divo2)/Div,
(100*didl+divo2)/Div
);
}
exit(EXIT_SUCCESS);
}

84
w.1 Normal file
View File

@ -0,0 +1,84 @@
.\" -*-Nroff-*-
.\"
.TH W 1 "8 Dec 1993 " " " "Linux User's Manual"
.SH NAME
w \- Show who is logged on and what they are doing.
.SH SYNOPSIS
.B w \-
.RB [ husfV ]
.RI [ user ]
.SH DESCRIPTION
.B "w "
displays information about the users currently on the machine,
and their processes.
The header shows, in this order, the current time,
how long the system has been running,
how many users are currently logged on,
and the system load averages for the past 1, 5, and 15 minutes.
.sp
The following entries are displayed for each user:
login name, the tty name, the remote host, login time, idle time, JCPU, PCPU,
and the command line of their current process.
.sp
The JCPU time is the time used by all processes attached to the tty. It
does not include past background jobs, but does include currently
running background jobs.
.sp
The PCPU time is the time used by the current process, named in the "what"
field.
.PP
.SH "COMMAND\-LINE OPTIONS"
.TP 0.5i
.B "\-h "
Don't print the header.
.TP 0.5i
.B "\-u "
Ignores the username while figuring out the current process and cpu
times. To demonstrate this, do a "su" and do a "w" and a "w -u".
.TP 0.5i
.B "\-s "
Use the short format.
Don't print the login time, JCPU or PCPU times.
.TP 0.5i
.B "\-f "
Toggle printing the
.B from
(remote hostname) field. The default as
released is for the
.B from
field to not be printed, although your system administrator or
distribution maintainer may have compiled a version in which the
.B from
field is shown by default.
.TP 0.5i
.B "\-V "
Display version information.
.TP 0.5i
.B "user "
Show information about the specified user only.
.SH FILES
.TP
.I /etc/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 <acahalan@cs.uml.edu>

298
w.c Normal file
View File

@ -0,0 +1,298 @@
/* w - show what logged in users are doing. Almost entirely rewritten from
* scratch by Charles Blake circa June 1996. Some vestigal traces of the
* original may exist. That was done in 1993 by Larry Greenfield with some
* fixes by Michael K. Johnson.
*/
#include "proc/version.h"
#include "proc/whattime.h"
#include "proc/readproc.h"
#include "proc/devname.h"
#include "proc/procps.h"
#include "proc/sysinfo.h"
#include <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 <sys/param.h>*/ /* for HZ */
static int ignoreuser = 0; /* for '-u' */
static proc_t **procs; /* our snapshot of the process table */
typedef struct utmp utmp_t;
#ifdef W_SHOWFROM
# define FROM_STRING "on"
#else
# define FROM_STRING "off"
#endif
/* Uh... same thing as UT_NAMESIZE */
#define USERSZ (sizeof u->ut_user)
/* This routine is careful since some programs leave utmp strings
* unprintable. Always outputs at least 16 chars padded with spaces
* on the right if necessary.
*/
static void print_host(char* host, int len) {
char *last;
int width = 0;
/* FIXME: there should really be a way to configure this... */
/* for now, we'll just limit it to the 16 that the libc5 version
* of utmp uses.
*/
if (len > 16) len = 16;
last = host + len;
for ( ; host < last ; host++){
if (isprint(*host) && *host != ' ') {
fputc(*host, stdout);
++width;
} else {
break;
}
}
if(!width){ /* blank fields screw up parsers */
fputc('-', stdout);
++width;
}
/* if *any* unprintables(or blanks), replace rest of line with spaces */
while (width < 16) {
fputc(' ', stdout);
++width;
}
}
/***** compact 7 char format for time intervals (belongs in libproc?) */
static void print_time_ival7(time_t t, int centi_sec, FILE* fout) {
if((long)t < (long)0){ /* system clock changed? */
printf(" ? ");
return;
}
if (t >= 48*60*60) /* > 2 days */
fprintf(fout, " %2ludays", t/(24*60*60));
else if (t >= 60*60) /* > 1 hour */
fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60));
else if (t > 60) /* > 1 minute */
fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
else
fprintf(fout, " %2lu.%02us", t, centi_sec);
}
/**** stat the device file to get an idle time */
static time_t idletime(char *tty) {
struct stat sbuf;
if (stat(tty, &sbuf) != 0)
return 0;
return time(NULL) - sbuf.st_atime;
}
/***** 7 character formatted login time */
static void print_logintime(time_t logt, FILE* fout) {
char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
*month [] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec" };
time_t curt;
struct tm *logtm, *curtm;
int hour;
char *merid; /* meridian indicator */
int today;
curt = time(NULL);
curtm = localtime(&curt);
/* localtime returns a pointer to static memory */
today = curtm->tm_yday;
logtm = localtime(&logt);
hour = logtm->tm_hour;
merid = (hour < 12) ? "am" : "pm";
if (hour >= 12) hour -= 12;
if (hour == 0) hour = 12;
if (curt - logt > 12*60*60 && logtm->tm_yday != today) {
if (curt - logt > 6*24*60*60)
fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon],
logtm->tm_year % 100);
else
fprintf(fout, " %3s%02d%s", weekday[logtm->tm_wday], hour, merid);
} else {
fprintf(fout, " %02d:%02d%s", hour, logtm->tm_min, merid);
}
}
/* This function scans the process table accumulating total cpu times for
* any processes "associated" with this login session. It also searches
* for the "best" process to report as "(w)hat" the user for that login
* session is doing currently. This the essential core of 'w'.
*/
static proc_t *getproc(utmp_t *u, char *tty, int *jcpu, int *found_utpid) {
int line;
proc_t **p, *best = NULL, *secondbest = NULL;
unsigned uid = ~0U;
if(!ignoreuser){
char buf[UT_NAMESIZE+1];
struct passwd *passwd_data; /* pointer to static data */
strncpy(buf,u->ut_user,UT_NAMESIZE);
buf[UT_NAMESIZE] = '\0';
passwd_data = getpwnam(buf);
if(!passwd_data) return NULL;
uid = passwd_data->pw_uid;
/* OK to have passwd_data go out of scope here */
}
line = tty_to_dev(tty);
*jcpu = *found_utpid = 0;
for(p = procs; *p; p++) {
if((**p).pid == u->ut_pid) *found_utpid = 1;
if((**p).tty != line) continue;
(*jcpu) += (**p).utime + (**p).stime;
secondbest = *p;
/* same time-logic here as for "best" below */
if(! (secondbest && (**p).start_time <= secondbest->start_time) ){
secondbest = *p;
}
if(!ignoreuser && uid != (**p).euid && uid != (**p).ruid) continue;
if((**p).pid != (**p).tpgid) continue;
if(best && (**p).start_time <= best->start_time) continue;
best = *p;
}
return best ? best : secondbest;
}
/***** showinfo */
static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
int jcpu, ut_pid_found;
unsigned i;
char uname[USERSZ + 1] = "",
tty[5 + sizeof u->ut_line + 1] = "/dev/";
proc_t *best;
for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */
if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/'))
tty[i+5] = u->ut_line[i];
else
tty[i+5] = '\0';
best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
/* just skip if stale utmp entry (i.e. login proc doesn't exist). If there
* is a desire a cmdline flag could be added to optionally show it with a
* prefix of (stale) in front of cmd or something like that.
*/
if (!ut_pid_found)
return;
strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */
if (formtype) {
printf("%-9.8s%-9.8s", uname, u->ut_line);
if (from)
print_host(u->ut_host, sizeof u->ut_host);
print_logintime(u->ut_time, stdout);
if (*u->ut_line == ':') /* idle unknown for xdm logins */
printf(" ?xdm? ");
else
print_time_ival7(idletime(tty), 0, stdout);
print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
if (best) {
int pcpu = best->utime + best->stime;
print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
} else
printf(" ? ");
} else {
printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
if (from)
print_host(u->ut_host, sizeof u->ut_host);
if (*u->ut_line == ':') /* idle unknown for xdm logins */
printf(" ?xdm? ");
else
print_time_ival7(idletime(tty), 0, stdout);
}
fputs(" ", stdout);
if (best) {
if (best->cmdline)
print_strlist(stdout, best->cmdline, " ", maxcmd);
else
printf("%*.*s", -maxcmd, maxcmd, best->cmd);
} else {
printf("-");
}
fputc('\n', stdout);
}
/***** main */
int main(int argc, char **argv) {
char *user = NULL;
utmp_t *u;
struct winsize win;
int header=1, longform=1, from=1, args, maxcmd=80, ch;
#ifndef W_SHOWFROM
from = 0;
#endif
for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
switch (ch) {
case 'h': header = 0; break;
case 'l': longform = 1; break;
case 's': longform = 0; break;
case 'f': from = !from; break;
case 'V': display_version(); exit(0);
case 'u': ignoreuser = 1; break;
default:
printf("usage: w -hlsufV [user]\n"
" -h skip header\n"
" -l long listing (default)\n"
" -s short listing\n"
" -u ignore uid of processes\n"
" -f toggle FROM field (default %s)\n"
" -V display version\n", FROM_STRING);
exit(1);
}
if ((argv[optind]))
user = (argv[optind]);
if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
maxcmd = win.ws_col;
if (maxcmd < 71) {
fprintf(stderr, "%d column window is too narrow\n", maxcmd);
exit(1);
}
maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
if (maxcmd < 3)
fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
procs = readproctab(PROC_FILLCMD | PROC_FILLUSR);
if (header) { /* print uptime and headers */
print_uptime();
printf("USER TTY ");
if (from)
printf("FROM ");
if (longform)
printf(" LOGIN@ IDLE JCPU PCPU WHAT\n");
else
printf(" IDLE WHAT\n");
}
utmpname(UTMP_FILE);
setutent();
while ((u=getutent())) {
if (u->ut_type == USER_PROCESS &&
(user ? !strncmp(u->ut_user, user, USERSZ) : *u->ut_user))
showinfo(u, longform, maxcmd, from);
}
endutent();
return 0;
}

82
watch.1 Normal file
View File

@ -0,0 +1,82 @@
.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual"
.SH NAME
watch \- execute a program periodically, showing output fullscreen
.SH SYNOPSIS
.B watch
.I [\-dhv] [\-n <seconds>] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=<seconds>] [\-\-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.
.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.

277
watch.c Normal file
View File

@ -0,0 +1,277 @@
/* 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>.
*/
#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>
static struct option longopts[] =
{
{ "differences", optional_argument, 0, 'd' },
{ "help", no_argument, 0, 'h' },
{ "interval", required_argument, 0, 'n' },
{ "version", no_argument, 0, 'v' },
{ 0, 0, 0, 0 }
};
static char usage[] = "Usage: %s [-dhnv] [--differences[=cumulative]] [--help] [--interval=<n>] [--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;
#define min(x,y) ((x) > (y) ? (y) : (x))
#define max(x,y) ((x) > (y) ? (x) : (y))
static void
do_usage(void)
{
fprintf(stderr, usage, progname);
exit(1);
}
static void
do_exit(int status) {
if (curses_started)
endwin();
exit(status);
}
/* signal handler */
static void
die(int notused)
{
(void)notused;
do_exit(0);
}
static void
winch_handler(int notused)
{
(void)notused;
screen_size_changed = 1;
}
static void
get_terminal_size(void)
{
struct winsize w;
if (ioctl(2, TIOCGWINSZ, &w) == 0)
{
if (w.ws_row > 0)
height = w.ws_row;
if (w.ws_col > 0)
width = w.ws_col;
}
}
int
main(int argc, char *argv[])
{
int optc;
int option_differences=0,
option_differences_cumulative=0,
option_help=0,
option_version=0;
int interval=2;
char *command;
int command_length=0; /* not including final \0 */
progname = argv[0];
while ((optc = getopt_long(argc, argv, "+d::hn:v", longopts, (int *) 0))
!= EOF)
{
switch (optc)
{
case 'd':
option_differences = 1;
if (optarg)
option_differences_cumulative = 1;
break;
case 'h':
option_help = 1;
break;
case 'n':
{
char *s;
interval = strtol(optarg, &s, 10);
if (!*optarg || *s)
do_usage();
}
break;
case 'v':
option_version = 1;
break;
default:
do_usage();
break;
}
}
if (option_version)
{
fprintf (stderr, "%s\n", VERSION);
if (!option_help)
exit(0);
}
if (option_help)
{
fprintf(stderr, usage, progname);
fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
exit(0);
}
if (optind >= argc)
do_usage();
command = strdup(argv[optind++]);
command_length = strlen(command);
for (;optind<argc;optind++)
{
int s = strlen(argv[optind]);
char *endp = &command[command_length];
*endp = ' ';
command_length += s + 1;
command = realloc(command, command_length+1);
strcpy(endp+1, argv[optind]);
}
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;
if (screen_size_changed)
{
get_terminal_size();
resizeterm(height, width);
clear();
/* redrawwin(stdscr); */
screen_size_changed = 0;
first_screen = 1;
}
/* left justify interval and command, right justify time, clipping all
to fit window width */
asprintf(&header, "Every %ds: %.*s",
interval, max(width-1, command_length), command);
mvaddstr(0, 0, header);
if (strlen(header) > width - tsl - 1)
mvaddstr(0, width - tsl - 4, "... ");
mvaddstr(0, width - tsl + 1, ts);
free(header);
if (!(p = popen(command, "r")))
{
perror("popen");
do_exit(2);
}
for (y=2; y<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')
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)
{
int oldch = inch();
char oldc = oldch & A_CHARTEXT;
attr = !first_screen
&& (c != oldc
|| (option_differences_cumulative
&& (oldch & A_ATTRIBUTES)));
}
if (attr)
standout();
addch(c);
if (attr)
standend();
}
}
pclose(p);
first_screen = 0;
refresh();
sleep(interval);
}
endwin();
return 0;
}