procps 010114
This commit is contained in:
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
|
Reference in New Issue
Block a user