procps 010114
This commit is contained in:
commit
03a9b5a30f
18
.cvsignore.patch
Normal file
18
.cvsignore.patch
Normal 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
81
BUGS
Normal 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
339
COPYING
Normal 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
481
COPYING.LIB
Normal 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
73
INSTALL
Normal 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
220
Makefile
Normal 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
645
NEWS
Normal 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
176
TODO
Normal 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
5
XConsole
Executable 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
45
free.1
Normal 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
80
free.c
Normal 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
216
gfinger.p
Normal 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
32
glibc.patch
Normal 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
96
kill.1
Normal 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
486
minimal.c
Normal 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
356
oldps.1
Normal 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
660
oldps.c
Normal 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
158
pgrep.1
Normal 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
605
pgrep.c
Normal 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);
|
||||
}
|
34
pmap.c
Normal file
34
pmap.c
Normal 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
7
proc/.cvsignore.patch
Normal 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
481
proc/COPYING
Normal 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
98
proc/Makefile
Normal 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
49
proc/alloc.c
Normal 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
304
proc/compare.c
Normal 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
9
proc/compare.h
Normal 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
211
proc/devname.c
Normal 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
7
proc/devname.h
Normal 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
548
proc/ksym.c
Normal 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
52
proc/output.c
Normal 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
27
proc/procps.h
Normal 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
74
proc/pwcache.c
Normal 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
543
proc/readproc.c
Normal 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
200
proc/readproc.h
Normal 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
234
proc/sig.c
Normal 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
19
proc/sig.h
Normal 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
29
proc/status.c
Normal 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
4
proc/status.h
Normal 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
325
proc/sysinfo.c
Normal 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
40
proc/sysinfo.h
Normal 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
15
proc/tree.h
Normal 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
43
proc/version.c
Normal 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
23
proc/version.h
Normal 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
89
proc/whattime.c
Normal 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
9
proc/whattime.h
Normal 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
16
procps.lsm
Normal 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
105
procps.spec
Normal 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
5
ps/.cvsignore.patch
Normal 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
481
ps/COPYING
Normal 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
53
ps/HACKING
Normal 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 < 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
35
ps/Makefile
Executable 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
28
ps/TRANSLATION
Normal 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
324
ps/common.h
Normal 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 " */
|
||||
#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
398
ps/display.c
Normal 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
106
ps/escape.c
Normal 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
422
ps/global.c
Normal 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
49
ps/help.c
Normal 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
35
ps/it
Normal 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
1608
ps/output.c
Normal file
File diff suppressed because it is too large
Load Diff
6
ps/p
Executable file
6
ps/p
Executable 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
1123
ps/parser.c
Normal file
File diff suppressed because it is too large
Load Diff
521
ps/ps.1
Normal file
521
ps/ps.1
Normal 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
26
ps/regression
Normal 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
146
ps/select.c
Normal 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
875
ps/sortformat.c
Normal 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
165
ps/stacktrace.c
Normal 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
118
skill.1
Normal 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
557
skill.c
Normal 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;
|
||||
}
|
||||
|
||||
|
74
sysctl.8
Normal file
74
sysctl.8
Normal 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
437
sysctl.c
Normal 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
51
sysctl.conf.5
Normal 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
6
t
Executable 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
50
tload.1
Normal 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
156
tload.c
Normal 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
730
tmp-junk.c
Normal 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
456
top.1
Normal 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>
|
6
top.desktop
Normal file
6
top.desktop
Normal 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
228
top.h
Normal 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
34
uptime.1
Normal 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
9
uptime.c
Normal 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
178
utmp.c
Normal 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
105
vmstat.8
Normal 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
278
vmstat.c
Normal 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
84
w.1
Normal 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
298
w.c
Normal 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
82
watch.1
Normal 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
277
watch.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user