blob: 73708c8add2c522bbdbdece9392d93b5ecfaa30a [file] [log] [blame]
Guide to Porting lsof 4 to Unix OS Dialects
**********************************************************************
| The latest release of lsof is always available via anonymous ftp |
| from lsof.itap.purdue.edu. Look in pub/lsof.README for its |
| location. |
**********************************************************************
Contents
How Lsof Works
/proc-based Linux Lsof -- a Different Approach
General Guidelines
Organization
Source File Naming Conventions
Coding Philosophies
Data Requirements
Dlsof.h and #include's
Definitions That Affect Compilation
Options: Common and Special
Defining Dialect-Specific Symbols and Global Storage
Coding Dialect-specific Functions
Function Prototype Definitions and the _PROTOTYPE Macro
The Makefile
The Mksrc Shell Script
The MkKernOpts Shell Script
Testing and the lsof Test Suite
Where Next?
How Lsof Works
--------------
Before getting on with porting guidelines, just a word or two about
how lsof works.
Lsof obtains data about open UNIX dialect files by reading the
kernel's proc structure information, following it to the related
user structure, then reading the open file structures stored
(usually) in the user structure. Typically lsof uses the kernel
memory devices, /dev/kmem, /dev/mem, etc. to read kernel data.
Lsof stores information from the proc and user structures in an
internal, local proc structure table. It then processes the open
file structures by reading the file system nodes that lie behind
them, extracting and storing relevant data in internal local file
structures that are linked to the internal local process structure.
Once all data has been gathered, lsof reports it from its internal,
local tables.
There are a few variants on this subject. Some systems don't have
just proc structures, but have task structures, too, (e.g., NeXTSTEP
and OSF/1 derivatives). For some dialects lsof gets proc structures
or process information (See "/proc-based Linux Lsof -- a Different
Approach) from files of the /proc file system. It's not necessary
for lsof to read user structures on some systems (recent versions
of HP-UX), because the data lsof needs can be found in the task or
proc structures. In the end lsof gathers the same data, just from
slightly different sources.
/proc-based Linux Lsof -- a Different Approach
==============================================
For a completely different approach to lsof construction, take a
look at the /proc-based Linux sources in .../dialects/linux/proc.
(The sources in .../dialects/linux/kmem are for a traditional lsof
that uses /dev/kmem to read information from kernel structures.)
The /proc-based lsof obtains all its information from the Linux
/proc file system. Consequently, it is relatively immune to changes
in Linux kernel structures and doesn't need to be re-compiled each
time the Linux kernel version changes.
There are some down-sides to the Linux /proc-based lsof:
* It must run setuid-root in order to be able to read the
/proc file system branches for all processes. In contrast,
the /dev/kmem-based Linux lsof usually needs only setgid
permission.
* It depends on the exact character format of /proc files, so
it is sensitive to changes in /proc file composition.
* It is limited to the information a /proc file system
implementor decides to provide. For example, if a
/proc/net/<protocol> file lacks an inode number, the
/proc-based lsof can't connect open socket files to that
protocol. Another deficiency is that the /proc-based may
not be able to report file offset (position) information,
when it isn't available in the /proc/<PID>/fd/ entry for a
file.
In contrast the /dev/kmem-based lsof has full access to
kernel structures and "sees" new data as soon as it appears.
Of course, that new data requires that lsof be recompiled
and usually also requires changes to lsof.
Overall the switch from a /dev/kmem base to a /proc one is an
advantage to Linux lsof. The switch was made at lsof revision 4.23
for Linux kernel versions 2.1.72 (approximately) and higher. The
reason I'm not certain at which Linux kernel version a /proc-based
lsof becomes possible is that the /proc additions needed to implement
it have been added gradually to Linux 2.1.x in ways that I cannot
measure.
/proc-based lsof functions in many ways the same as /dev/kmem-based
lsof. It scans the /proc directory, looking for <PID>/ subdirectories.
Inside each one it collects process-related data from the cwd, exe,
maps, root, and stat information files.
It collects open file information from the fd/ subdirectory of each
<PID>/ subdirectory. The lstat(2), readlink(2), and stat(2) system
calls gather information about the files from the kernel.
Lock information comes from /proc/locks. It is matched to open
files by inode number. Mount information comes from /proc/mounts.
Per domain protocol information comes from the files of /proc/net;
it's matched to open socket files by inode number.
The Linux /proc file system implementors have done an amazing job
of providing the information lsof needs. The /proc-based lsof
project has so far generated only two kernel modification:
* A modification to /usr/src/linux/net/ipx/af_ipx.c adds the
inode number to the entries of /proc/net/ipx.
Jonathan Sergent did this kernel modification.
It may be found in the .../dialects/linux/proc/patches
subdirectory of the lsof distribution.
* An experimental modification to /usr/src/linux/fs/stat.c
allows lstat(2) to return file position information for
/proc/<PID>/fd/<FD> files.
Contact me for this modification.
One final note about the /proc-based Linux lsof: it doesn't need
any functions from the lsof library in the lib/ subdirectory.
General Guidelines
------------------
These are the general guidelines for porting lsof 4 to a new Unix
dialect:
* Understand the organization of the lsof sources and the
philosophies that guide their coding.
* Understand the data requirements and determine the methods
of locating the necessary data in the new dialect's kernel.
* Pick a name for the subdirectory in lsof4/dialects for your
dialect. Generally I use a vendor operating system name
abbreviation.
* Locate the necessary header files and #include them in the
dialect's dlsof.h file. (You may not be able to complete
this step until you have coded all dialect-specific functions.)
* Determine the optional library functions of lsof to be used
and set their definitions in the dialect's machine.h file.
* Define the dialect's specific symbols and global storage
in the dialect's dlsof.h and dstore.c files.
* Code the dialect-specific functions in the appropriate
source files of the dialect's subdirectory.
Include the necessary prototype definitions of the dialect-
specific functions in the dproto.h file in the dialect's
subdirectory.
* Define the dialect's Makefile and source construction shell
script, Mksrc.
* If there are #define's that affect how kernel structures
are organized, and those #define's are needed when compiling
lsof, build a MkKernOpts shell script to locate the #define's
and supply them to the Configure shell script.
Organization
------------
The code in a dialect-specific version of lsof comes from three
sources:
1) functions common to all versions, located in the top level
directory, lsof4;
2) functions specific to the dialect, located in the dialect's
subdirectory -- e.g., lsof4/dialects/sun;
3) functions that are common to several dialects, although
not to all, organized in a library, liblsof.a. The functions
in the library source can be selected and customized with
definitions in the dialect machine.h header files.
The tree looks like this:
lsof4 ----------------------+ 3) library --
| \ lsof4/lib
1) fully common functions + \
e.g., lsof4/main.c + lsof4/dialects/
/ / / / \
+ + + + +
2) dialect-specific subdirectories -- e.g., lsof4/dialects/sun
The code for a dialect-specific version is constructed from these
three sources by the Configure shell script in the top level lsof4
directory and definitions in the dialect machine.h header files.
Configure uses the Mksrc shell script in each dialect's subdirectory,
and may use an optional MkKernOpts shell script in selected dialect
subdirectories.
Configure calls the Mksrc shell script in each dialect's subdirectory
to assemble the dialect-specific sources in the main lsof directory.
Configure may call MkKernOpts to determine kernel compile-time
options that are needed for compiling kernel structures correctly
for use by lsof. Configure puts the options in a dialect-specific
Makefile it build, using a template in the dialect subdirectory.
The assembly of dialect-specific sources in the main lsof directory
is usually done by creating symbolic links from the top level to
the dialect's subdirectory. The LSOF_MKC environment variable may
be defined prior to using Configure to change the technique used
to assemble the sources -- most commonly to use cp instead of ln -s.
The Configure script completes the dialect's Makefile by adding
string definitions, including the necessary kernel compile-time
options, to a dialect skeleton Makefile while copying it from the
dialect subdirectory to the top level lsof4 directory. Optionally
Makefile may call the dialect's MkKernOpts script to add string
definitions.
When the lsof library, lsof4/lib/liblsof.a, is compiled its
functions are selected and customized by #define's in the dialect
machine.h header file.
Source File Naming Conventions
------------------------------
With one exception, dialect-specific source files begin with a
lower case `d' character -- ddev.c, dfile.c, dlsof.h. The one
exception is the header file that contains dialect-specific
definitions for the optional features of the common functions.
It's called machine.h for historical reasons.
Currently all dialects use almost the same source file names. One
exception to the rule happens in dialects where there must be
different source files -- e.g., dnode[123].c -- to eliminate node
header file structure element name conflicts. The source modules
in a few subdirectories are organized that way.
Unusual situations occur for NetBSD and OpenBSD, and for NEXTSTEP
and OPENSTEP. Each pair of dialects is so close in design that
the same dialect sources from the n+obsd subdirectory serves NetBSD
and OpenBSD; from n+os, NEXTSTEP and OPENSTEP.
These are common files in lsof4/:
Configure the configuration script
Customize does some customization of the selected lsof
dialect
Inventory takes an inventory of the files in an lsof
distribution
version the version number
dialects/ the dialects subdirectory
These are the common function source files in lsof4/:
arg.c common argument processing functions
lsof.h common header file that #include's the dialect-specific
header files
main.c common main function for lsof 4
misc.c common miscellaneous functions -- e.g., special versions
of stat() and readlink()
node.c common node reading functions -- readinode(), readvnode()
print.c common print support functions
proc.c common process and file structure functions
proto.h common prototype definitions, including the definition of
the _PROTOTYPE() macro
store.c common global storage version.h the current lsof version
number, derived from the file version by the Makefile
usage.c functions to display lsof usage panel
These are the dialect-specific files:
Makefile the Makefile skeleton
Mksrc a shell script that assists the Configure script
in configuring dialect sources
MkKernOpts an optional shell script that identifies kernel
compile-time options for selected dialects -- e.g.,
Pyramid DC/OSx and Reliant UNIX
ddev.c device support functions -- readdev() -- may be
eliminated by functions from lsof4/lib/
dfile.c file processing functions -- may be eliminated by
functions from lsof4/lib/
dlsof.h dialect-specific header file -- contains #include's
for system header files and dialect-specific global
storage declarations
dmnt.c mount support functions -- may be eliminated by
functions from lsof4/lib/
dnode.c node processing functions -- e.g., for gnode or vnode
dnode?.c additional node processing functions, used when node
header files have duplicate and conflicting element
names.
dproc.c functions to access, read, examine and cache data about
dialect-specific process structures -- this file contains
the dialect-specific "main" function, gather_proc_info()
dproto.h dialect-specific prototype declarations
dsock.c dialect-specific socket processing functions
dstore.c dialect-specific global storage -- e.g., the nlist()
structure
machine.h dialect specific definitions of common function options --
e.g., a HASINODE definition to activate the readinode()
function in lsof4/node.c
The machine.h header file also selects and customizes
the functions of lsof4/lib/.
These are the lib/ files. Definitions in the dialect machine.h
header files select and customize the contained functions that are
to be compiled and archived to liblsof.a.
Makefile.skel is a skeleton Makefile, used by Configure
to construct the Makefile for the lsof
library.
cvfs.c completevfs() function
USE_LIB_COMPLETEVFS selects it.
CVFS_DEVSAVE, CVFS_NLKSAVE, CVFS_SZSAVE,
and HASFSINO customize it.
dvch.c device cache functions
HASDCACHE selects them.
DCACHE_CLONE, DCACHE_CLR, DCACHE_PSEUDO,
DVCH_CHOWN, DVCH_DEVPATH, DVCH_EXPDEV,
HASBLKDEV, HASENVDC, HASSYSDC, HASPERSDC,
HASPERSDCPATH, and NOWARNBLKDEV customize
them.
fino.c find block and character device inode functions
HASBLKDEV and USE_LIB_FIND_CH_INO select them.
isfn.c hashSfile() and is_file_named() functions
USE_LIB_IS_FILE_NAMED selects it.
lkud.c device lookup functions
HASBLKDEV and USE_LIB_LKUPDEV select them.
pdvn.c print device name functions
HASBLKDEV and USE_LIB_PRINTDEVNAME select them.
prfp.c process_file() function
USE_LIB_PROCESS_FILE selects it.
FILEPTR, DTYPE_PIPE, HASPIPEFN, DTYPE_GNODE,
DTYPE_INODE, DTYPE_PORT, DTYPE_VNODE,
HASF_VNODE, HASKQUEUE, HASPRIVFILETYPE,
HASPSXSHM and HASPSXSEM customize it.
ptti.c print_tcptpi() function
USE_LIB_PRINT_TCPTPI selects it.
HASSOOPT, HASSBSTATE, HASSOSTATE, AHSTCPOPT,
HASTCPTPIQ and HASTCPTPIW customize it.
rdev.c readdev() function
USE_LIB_READDEV selects it.
DIRTYPE, HASBLKDEV, HASDCACHE, HASDNAMLEN,
RDEV_EXPDEV, RDEV_STATFN, USE_STAT, and
WARNDEVACCESS customize it.
rmnt.c readmnt() function
USE_LIB_READMNT selects it.
HASFSTYPE, MNTSKIP, RMNT_EXPDEV, RMNT_FSTYPE,
and MOUNTS_FSTYPE customize it.
rnam.c BSD format name cache functions
HASNCACHE and USE_LIB_RNAM select them.
HASFSINO, NCACHE, NCACHE_NC_CAST, NCACHE_NM,
NCACHE_NMLEN, NCACHE_NODEADDR, NCACHE_NODEID,
NCACHE_NO_ROOT, NCACHE_NXT, NCACHE_PARADDR,
NCACHE_PARID, NCACHE_SZ_CAST, NCHNAMLEN,
X_NCACHE, and X_NCSIZE, customize them.
rnch.c Sun format name cache functions
HASNCACHE and USE_LIB_RNCH select them.
ADDR_NCACHE, HASDNLCPTR, HASFSINO, NCACHE_DP,
NCACHE_NAME, NCACHE_NAMLEN, NCACHE_NEGVN,
NCACHE_NODEID, NCACHE_NXT, NCACHE_PARID,
NCACHE_VP, X_NCACHE, and X_NCSIZE, customize
them.
snpf.c Source for the snprintf() family of functions
USE_LIB_SNPF selects it.
The comments and the source code in these library files give more
information on customization.
Coding Philosophies
-------------------
A few basic philosophies govern the coding of lsof 4 functions:
* Use as few #if/#else/#endif constructs as possible, even at
the cost of nearly-duplicate code.
When #if/#else/#endif constructs are necessary:
o Use the form
#if defined(s<symbol>)
in preference to
#ifdef <symbol>
to allow easier addition of tests to the #if.
o Indent them to signify their level -- e.g.,
#if /* level one */
# if /* level two */
# endif /* level two */
#else /* level one */
#endif /* level one */
o Use ANSI standard comments on #else and #endif statements.
* Document copiously.
* Aim for ANSI-C compatibility:
o Use function prototypes for all functions, hiding them
from compilers that cannot handle them with the _PROTOTYPE()
macro.
o Use the compiler's ANSI conformance checking wherever
possible -- e.g., gcc's -ansi option.
Data Requirements
-----------------
Lsof's strategy in obtaining open file information is to access
the process table via its proc structures, then obtain the associated
user area and open file structures. The open file structures then
lead lsof to file type specific structures -- cdrnodes, fifonodes,
inodes, gnodes, hsfsnodes, pipenodes, pcnodes, rnodes, snodes,
sockets, tmpnodes, and vnodes.
The specific node structures must yield data about the open files. The
most important items and device number (raw and cooked) and node
number. (Lsof uses them to identify files and file systems named as
arguments.) Link counts and file sizes are important, too, as are the
special characteristics of sockets, pipes, FIFOs, etc.
This means that to begin an lsof port to a new Unix dialect you
must understand how to obtain these structures from the dialect's
kernel. Look for kernel access functions -- e.g., the AIX readx()
function, Sun and Sun-like kvm_*() functions, or SGI's syssgi()
function. Look for clues in header files -- e.g. external declarations
and macros.
If you have access to them, look at sources to programs like ps(1),
or the freely available monitor and top programs. They may give
you important clues on reading proc and user area structures. An
appeal to readers of dialect-specific news groups may uncover
correspondents who can help.
Careful reading of system header files -- e.g., <sys/proc.h> --
may give hints about how kernel storage is organized. Look for
global variables declared under a KERNEL or _KERNEL #if. Run nm(1)
across the kernel image (/vmunix, /unix, etc.) and look for references
to structures of interest.
Even if there are support functions for reading structures, like the
kvm_*() functions, you must still understand how to read data from
kernel memory. Typically this requires an understanding of the
nlist() function, and how to use /dev/kmem, /dev/mem, and /dev/swap.
Don't overlook the possibility that you may have to use the process
file system -- e.g., /proc. I try to avoid using /proc when I can,
since it usually requires that lsof have setuid(root) permission
to read the individual /proc "files".
Once you can access kernel structures, you must understand how
they're connected. You must answer questions like:
* How big are kernel addresses? How are they type cast?
* How are kernel variable names converted to addresses?
Nlist()?
* How are the proc structures organized? Is it a static
table? Are the proc structures linked? Is there a
kernel pointer to the first proc structure? Is there a
proc structure count?
* How does one obtain copies of the proc structures? Via
/dev/kmem? Via a vendor API?
* If this is a Mach derivative, is it necessary to obtain the
task and thread structures? How?
* How does one obtain the user area (or the utask area in Mach
systems) that corresponds to a process?
* Where are the file structures located for open file
descriptors and how are they located? Are all file
structures in the user area? Is the file structure space
extensible?
* Where do the private data pointers in file structures lead?
To gnodes? To inodes? To sockets? To vnodes? Hint: look
in <sys/file.h> for DTYPE_* instances and further pointers.
* How are the nodes organized? To what other nodes do they
lead and how? Where are the common bits of information in
nodes -- device, node number, size -- stored? Hint: look
in the header files for nodes for macros that may be used
to obtain the address of one node from another -- e.g., the
VTOI() macro that leads from a vnode to an inode.
* Are text reference nodes identified and how? Is it
necessary to examine the virtual memory map of a process or
a task to locate text references? Some kernels have text
node pointers in the proc structures; some, in the user
area; Mach kernels may have text information in the task
structure, reached in various ways from the proc, user area,
or user task structure.
* How is the device table -- e.g., /dev or /devices --
organized? How is it read? Using direct or dirent structures?
How are major/minor device numbers represented? How are
device numbers assembled and disassembled?
Are there clone devices? How are they identified?
* How is mount information obtained? Getmntinfo()? Getmntent()?
Some special kernel call?
* How are sockets identified and organized? BSD-style? As
streams? Are there streams?
* Are there special nodes -- CD-ROM nodes, FIFO nodes, etc.?
* How is the kernel's name cache organized? Can lsof access
it to get partial name components?
Dlsof.h and #include's
----------------------
Once you have identified the kernel's data organization and know
what structures it provides, you must add #include's to dlsof.h to
access their definitions. Sometimes it is difficult to locate the
header files -- you may need to introduce -I specifications in the
Makefile via the DINC shell variable in the Configure script.
Sometimes it is necessary to define special symbols -- e.g., KERNEL,
_KERNEL, _KMEMUSER -- to induce system header files to yield kernel
structure definitions. Sometimes making those symbol definitions
cause other header file and definition conflicts. There's no good
general rule on how to proceed when conflicts occur.
Rarely it may be necessary to extract structure definitions from
system header files and move them to dlsof.h, create special versions
of system header files, or obtain special copies of system header
files from "friendly" (e.g., vendor) sources. The dlsof.h header
file in lsof4/dialects/sun shows examples of the first case; the
second, no examples; the third, the irix5hdr subdirectory in
lsof4/dialects/irix (a mixture of the first and third).
Building up the necessary #includes in dlsof.h is an iterative
process that requires attention as you build the dialect-specific
functions that references kernel structures. Be prepared to revisit
dlsof.h frequently.
Definitions That Affect Compilation
-----------------------------------
The source files at the top level and in the lib/ subdirectory
contain optional functions that may be activated with definitions
in a dialect's machine.h header file. Some are functions for
reading node structures that may not apply to all dialects -- e.g.
CD-ROM nodes (cdrnode), or `G' nodes (gnode) -- and others are
common functions that may occasionally be replaced by dialect-specific
ones. Once you understand your kernel's data organization, you'll
be able to decide the optional common node functions to activate.
Definitions in machine.h and dlsof.h also enable or disable other
optional common features. The following is an attempt to list all
the definitions that affect lsof code, but CAUTION, it is only
attempt and may be incomplete. Always check lsof4 source code in
lib/ and dialects/, and dialect machine.h header files for other
possibilities
AFS_VICE See 00XCONFIG.
AIX_KERNBITS specifies the kernel bit size, 32 or 64, of the Power
architecture AIX 5.x kernel for which lsof was built.
CAN_USE_CLNT_CREATE is defined for dialects where the more modern
RPC function clnt_create() can be used in
place of the deprecated clnttcp_create().
CLONEMAJ defines the name of the variable that
contains the clone major device number.
(Also see HAS_STD_CLONE and HAVECLONEMAJ.)
DEVDEV_PATH defines the path to the directory where device
nodes are stored, usually /dev. Solaris 10
uses /devices.
DIALECT_WARNING may be defined by a dialect to provide a
warning message that will be displayed with
help (-h) and version (-v) output.
FSV_DEFAULT defines the default file structure values to
list. It may be composed of or'd FSV_*
(See lsof.h) values. The default is none (0).
GET_MAJ_DEV is a macro to get major portion from device
number instead of via the standard major()
macro.
GET_MIN_DEV is a macro to get minor portion from device
number instead of via the standard minor()
macro.
GET_MAX_FD the name of the function that returns an
int for the maximum open file descriptor
plus one. If not defined, defaults to
getdtablesize.
HAS9660FS enables CD9660 file system support in a
BSD dialect.
HAS_ADVLOCK_ARGS is defined for NetBSD and OpenBSD dialects
whose <sys/lockf.h> references vop_advlock_args.
HAS_AFS enables AFS support code for the dialect.
HAS_ATOMIC_T indicates the Linux version has an
<asm/atomic.h> header file and it contains
"typedef struct .* atomic_t;"
HASAOPT indicates the dialect supports the AFS -A
option when HAS_AFS is also defined.
HAS_ASM_TERMIOBITS indicates for Linux Alpha that the
<asm/termiobits.h> header file exists.
HASAX25CBPTR indicates that the Linux sock struct has an
ax25_db pointer.
HASBLKDEV indicates the dialect has block device support.
HASBUFQ_H indicates the *NSD dialect has the <sys/bufq.h>
header file.
HASCACHEFS enables cache file system support for the
dialect.
HAS_CDFS enables CDFS file system support for the
dialect.
HASCDRNODE enables/disables readcdrnode() in node.c.
HAS_CONN_NEW indicates the Solaris version has the new form
of the conn_s structure, introduced in b134 of
Solaris 11. This will always accompany the
HAS_IPCLASSIFIER_H definition.
HAS_CONST indicates that the compiler supports the
const keyword.
HASCPUMASK_T indicates the FreeBSD 5.2 or higher dialect
has cpumask_t typedef's.
HAS_CRED_IMPL_H indicates the Solaris 10 dialect has the
<sys/cred_impl.h> header file available.
HASCWDINFO indicates the cwdinfo structure is defined
in the NetBSD <sys/filedesc.h>.
HASDCACHE enables device file cache file support.
The device cache file contains information
about the names, device numbers and inode
numbers of entries in the /dev (or /device)
node subtree that lsof saves from call to
call. See the 00DCACHE file of the lsof
distribution for more information on this
feature.
HASDENTRY indicates the Linux version has a dentry
struct defined in <linux/dcache.h>.
HASDEVKNC indicates the Linux version has a kernel
name cached keyed on device number.
HAS_DINODE_U indicates the OpenBSD version has a dinode_u
union in its inode structure.
HASDNLCPTR is defined when the name cache entry of
<sys/dnlc.h> has a name character pointer
rather than a name character array.
HASEFFNLINK indicates the *BSD system has the i_effnlink
member in the inode structure.
HASENVDC enables the use of an environment-defined
device cache file path and defines the name
of the environment variable from which lsof
may take it. (See the 00DCACHE file of
the lsof distribution for information on
when HASENVDC is used or ignored.)
HASEOPT indicates the dialect supports the -e option to
eliminate kernel blocks on a named file system.
HASEXT2FS is defined for BSD dialects for which ext2fs
file system support can be provided. A value
of 1 indicates that the i_e2din member does not
exist; 2, it exists.
HASF_VNODE indicates the dialect's file structure has an
f_vnode member in it.
HASFDESCFS enables file descriptor file system support
for the dialect. A value of 1 indicates
<miscfs/fdesc.h> has a Fctty definition; 2,
it does not.
HASFDLINK indicates the file descriptor file system
node has the fd_link member.
HASFIFONODE enables/disables readfifonode() in node.c.
HAS_FL_FD indicates the Linux version has an fl_fd
element in the lock structure of <linux/fs.h>.
HAS_FL_FILE indicates the Linux version has an fl_file
element in the lock structure of <linux/fs.h>.
HAS_FL_WHENCE indicates the Linux version has an fl_whence
element in the lock structure of <linux/fs.h>.
HAS_F_OPEN indicates the UnixWare 7.x dialect has the
f_open member in its file struct.
HASFSINO enables the inclusion of the fs_ino element
in the lfile structure definition in lsof.h.
This contains the file system's inode number
and may be needed when searching the kernel
name cache. See dialects/osr/dproc.c for
an example.
HAS_JFS2 The AIX >= 5.0 dialect has jfs2 support.
HASFSTRUCT indicates the dialect has a file structure
the listing of whose element values can be
enabled with +f[cfn]. FSV_DEFAULT defines
the default listing values.
HASFSTYPE enables/disables the use of the file system's
stat(2) st_fstype member.
If the HASFSTYPE value is 1, st_fstype is
treated as a character array; 2, it is
treated as an integer.
See also the RMNT_EXPDEV and RMNT_FSTYPE
documentation in lib/rmnt.c
HASGETBOOTFILE indicates the NetBSD or OpenBSD dialect has
a getbootfile() function.
HASGNODE enables/disables readgnode() in node.c.
HASHASHPID is defined when the Linux version (probably
above 2.1.35) has a pidhash_next member in
its task structure.
HASHSNODE enables/disables readhsnode() in node.c.
HASI_E2FS_PTR indicates the BSD dialect has a pointer in
its inode to the EXTFS dinode.
HASI_FFS indicates the BSD dialect has i_ffs_size
in <ufs/ufs/inode.h>.
HASI_FFS1 indicates the BSD dialect supports the fast
UFS1 and UFS2 file systems.
HAS_INKERNEL indicates the SCO OSR 6.0.0 or higher, or
UnixWare 7.1.4 or higher system uses the
INKERNEL symbol in <netinet/in_pcb.h> or
<netinet/tcp_var.h>.
HASINODE enables/disables readinode() in node.c.
HASINOKNC indicates the Linux version has a kernel
name cache keyed on inode address.
HASINADDRSTR is defined when the inp_[fl]addr members
of the inpcb structure are structures.
HASINRIAIPv6 is defined if the dialect has the INRIA IPv6
support. (HASIPv6 will also be defined.)
HASINT16TYPE is defined when the dialect has a typedef
for int16 that may conflict with some other
header file's redefinition (e.g., <afs/std.h>).
HASINT32TYPE is defined when the dialect has a typedef
for int32 that may conflict with some other
header file's redefinition (e.g., <afs/std.h>).
HASINTSIGNAL is defined when signal() returns an int.
HAS_IPCLASSIFIER_H is defined for Solaris dialects that have the
<inet/ipclassifier.h> header file.
HAS_IPC_S_PATCH is defined when the HP-UX 11 dialect has the
ipc_s patch installed. It has a value of
1 if the ipc_s structure has an ipc_ipis
member, but the ipis_s structure lacks the
ipis_msgsqueued member; 2, if ipc_s has
ipc_ipis, but ipis_s lacks ipis_msgsqueued.
HASIPv6 indicates the dialect supports the IPv6
Internet address family.
HASKERNELKEYT indicates the Linux version has a
__kernel_key_t typedef in <linux/types.h>.
HASKERNFS is defined for BSD dialects for which
/kern file system support can be provided.
HASKERNFS_KFS_KT indicates *kfs_kt is in the BSD dialect's
<miscfs/kernfs/kernfs.h>.
HASKOPT enables/disables the ability to read the
kernel's name list from a file -- e.g., from
a crash dump file.
HASKQUEUE indicates the dialect supports the kqueue
file type.
HASKVMGETPROC2 The *BSD dialect has the kvm_gettproc2()
function.
HAS_KVM_VNODE indicates the FreeBSD 5.3 or higher dialect has
"defined(_KVM_VNODE)" in <sys/vnode.h>.
HASLFILEADD defines additional, dialect-specific elements
SETLFILEADD in the lfile structure (defined in lsof.h).
HASLFILEADD is a macro. The accompanying SETFILEADD
macro is used in the alloc_lfile() function of
proc.c to preset the additional elements.
HAS_LF_LWP is defined for BSD dialects where the lockf
structure has an lf_lwp member.
HASLFS indicates the *BSD dialect has log-structured
file system support.
HAS_LGRP_ROOT_CONFLICT
indicates the Solaris 9 or Solaris 10 system has
a conflict over the lgrp_root symbol in the
<sys/lgrp.h> and <sys/lgrp_user.h> header files.
HAS_LIBCTF indicates the Solaris 10 and above system has
the CTF library.
HAS_LOCKF_ENTRY indicates the FreeBSD version has a lockf_entry
structure in its <sys/lockf.h> header file.
HAS_LWP_H is defined for BSD dialects that have the
<sys/lwp.h> header file.
HASMOPT enables/disables the ability to read kernel
memory from a file -- e.g., from a crash
dump file.
HASMSDOSFS enables MS-DOS file system support in a
BSD dialect.
HASMNTSTAT indicates the dialect has a stat(2) status
element in its mounts structure.
HASMNTSUP indicates the dialect supports the mount supplement
option.
HASNAMECACHE indicates the FreeBSD dialect has a namecache
structure definition in <sys/namei.h>.
HASNCACHE enables the probing of the kernel's name cache
to obtain path name components. A value
of 1 directs printname() to prefix the
cache value with the file system directory
name; 2, avoid the prefix.
HASNCVPID The *BSD dialect namecache struct has an
nc_vpid member.
HASNETDEVICE_H indicates the Linux version has a netdevice.h
header file.
HAS_NFS enables NFS support for the dialect.
HASNFSKNC indicates the LINUX version has a separate
NFS name cache.
HASNFSPROTO indicates the NetBSD or OpenBSD version
has the nfsproto.h header file.
HASNFSVATTRP indicates the n_vattr member of the nfsnode of
the *BSD dialect is a pointer.
HASNLIST enables/disables nlist() function support.
(See NLIST_TYPE.)
HASNOFSADDR is defined if the dialect has no file structure
addresses. (HASFSTRUCT must be defined.)
HASNOFSCOUNT is defined if the dialect has no file structure counts.
(HASFSTRUCT must be defined.)
HASNOFSFLAGS is defined if the dialect has no file structure flags.
(HASFSTRUCT must be defined.)
HASNOFSNADDR is defined if the dialect has no file structure node
addresses. (HASFSTRUCT must be defined.)
HAS_NO_6PORT is defined if the FreeBSD in_pcb.h has no in6p_.port
definitions.
HAS_NO_6PPCB is defined if the FreeBSD in_pcb.h has no in6p_ppcb
definition.
HAS_NO_ISO_DEV indicates the FreeBSD 6 and higher system has
no i_dev member in its iso_node structure.
HAS_NO_LONG_LONG indicates the dialect has no support for the C
long long type. This definition is used by
the built-in snprintf() support of lib/snpf.c.
HASNORPC_H indicates the dialect has no /usr/include/rpc/rpc.h
header file.
HAS_NO_SI_UDEV indicates the FreeBSD 6 and higher system has
no si_udev member in its cdev structure.
HASNOSOCKSECURITY enables the listing of open socket files,
even when HASSECURITY restricts listing of
open files to the UID of the user who is
running lsof, provided socket file listing
is selected with the "-i" option. This
definition is only effective when HASSECURITY
is also defined.
HASNULLFS indicates the dialect (usually *BSD) has a
null file system.
HASOBJFS indicates the Pyramid version has OBJFS
support.
HASONLINEJFS indicates the HP-UX 11 dialect has the optional
OnlineJFS package installed.
HAS_PC_DIRENTPERSEC
indicates the Solaris 10 system's <sys/fs/pc_node.h>
header file has the pc_direntpersec() macro.
HAS_PAD_MUTEX indicates the Solaris 11 system has the pad_mutex_t
typedef in its <sys/mutex.h> header file.
HASPERSDC enables the use of a personal device cache
file path and specifies a format by which
it is constructed. See the 00DCACHE file
of the lsof distribution for more information
on the format.
HASPERSDCPATH enables the use of a modified personal
device cache file path and specifies the
name of the environment variable from which
its component may be taken. See the 00DCACHE
file of the lsof distribution for more
information on the modified personal device
cache file path.
HASPINODEN declares that the inode number of a /proc file
should be stored in its procfsid structure.
HASPIPEFN defines the function that processes DTYPE_PIPE
file structures. It's used in the prfp.c
library source file. See the FreeBSD
dialect source for an example.
HASPIPENODE enables/disables readpipenode() in node.c.
HASPMAPENABLED enables the automatic reporting of portmapper
registration information for TCP and UDP
ports that have been registered.
HASPPID indicates the dialect has parent PID support.
HASPR_LDT indicates the Solaris dialect has a pr_ldt
member in the pronodetype enum.
HASPR_GWINDOWS indicates the Solaris dialect has a pr_windows
member in the pronodetype enum.
HASPRINTDEV this value defines a private function for
printing the dialect's device number. Used
by print.c/print_file(). Takes one argument:
char *HASPRINTDEV(struct lfile *)
HASPRINTINO this value names a private function for
printing the dialect's inode number. Used
by print.c/print_file(). Takes one argument:
char *HASPRINTINO(struct lfile *)
HASPRINTNM this value names a private function for
printing the dialect's file name. Used by
print.c/print_file(). Takes one argument:
void HASPRINTNM(struct lfile *)
HASPRINTOFF this value names a private function for
printing the dialect's file offset. Used
by print.c/print_file(). Takes two arguments:
char *HASPRINTOFF(struct lfile *, int ty)
Where ty == 0 if the offset is to be printed
in 0t<decimal> format; 1, 0x<hexadecimal>.
HASPRINTSZ this value names a private function for
printing the dialect's file size. Used
by print.c/print_file(). Takes one argument:
char *HASPRINTSZ(struct lfile *)
void HASPRINTNM(struct lfile *)
HASPRIVFILETYPE enables processing of the private file
type, whose number (from f_type of the file
struct) is defined by PRIVFILETYPE.
HASPRIVFILETYPE defines the function that
processes the file struct's f_data member.
Processing is initiated from the process_file()
function of the prfp.c library source file
or from the dialect's own process_file()
function.
HASPRIVNMCACHE enables printing of a file path from a
private name cache. HASPRIVNMCACHE defines
the name of the printing function. The
function takes one argument, a struct lfile
pointer to the file, and returns non-zero
if it prints a cached name to stdout.
HASPRIVPRIPP is defined for dialects that have a private
function for printing the IP protocol name.
When this is not defined, the function to
do that defaults to printiproto().
HASPROCFS defines the name (if any) of the process file
system -- e.g., /proc.
HASPROCFS_PFSROOT indicates PFSroot is in the BSD dialect's
<miscfs/procfs/procfs.h>.
HASPSEUDOFS indicates the FreeBSD dialect has pseudofs
file system support.
HASPSXSEM indicates the dialect has support for the POSIX
semaphore file type.
HASPSXSHM indicates the dialect has support for the POSIX
shared memory file type.
HASPTYFS indicates the *BSD dialect has a ptyfs file system.
HASRNODE enables/disables readrnode() in node.c.
HASRNODE3 indicates the HPUX 10.20 or lower dialect has NFS3
support with a modified rnode structure.
HASRPCV2H The FreeBSD dialect has <nfs/rpcv2.h>.
HAS_SANFS indicates the AIX system has SANFS file system
support.
HASSBSTATE indicates the dialect has socket buffer state
information (e.g., SBS_* symbols) available.
HASSECURITY enables/disables restricting open file
information access. (Also see HASNOSOCKSECURITY.)
HASSELINUX indicates the Linux dialect has SELinux security
context support available.
HASSETLOCALE is defined if the dialect has <locale.h> and
setlocale().
HAS_SI_PRIV indicates the FreeBSD 6.0 and higher cdev
structure has an si_priv member.
HAS_SOCKET_PROTO_H indicates the Solaris 10 system has the header file
<sys/socket_proto.h>.
HASSOUXSOUA indicates that the Solaris <sys/socketvar.h> has
soua_* members in its so_ux_addr structure.
HASSPECDEVD indicates the dialect has a special device
directory and defines the name of a function
that processes the results of a successful
stat(2) of a file in that directory.
HASSPECNODE indicates the DEC OSF/1, or Digital UNIX,
or Tru64 UNIX <sys/specdev.h> has a spec_node
structure definition.
HASSNODE indicates the dialect has snode support.
HAS_SOCKET_SK indicates that the Linux socket structure
has the ``struct sock *sk'' member.
HASSOOPT indicates the dialect has socket option
information (e.g., SO_* symbols) available.
HASSOSTATE indicates the dialect has socket state
information (e.g., SS_* symbols) available.
HASSTATVFS indicates the NetBSD dialect has a statvfs
struct definition.
HASSTAT64 indicates the dialect's <sys/stat.h> contains
stat64.
HAS_STD_CLONE indicates the dialect uses a standard clone
device structure that can be used in common
library function clone processing. If the
value is 1, the clone table will be built
by readdev() and cached when HASDCACHE is
defined; if the value is 2, it is assumed
the clone table is built independently.
(Also see CLONEMAJ and HAVECLONEMAJ.)
HASSTREAMS enables/disables streams. CAUTION, requires
specific support code in the dialect sources.
HAS_STRFTIME indicates the dialect has the gmtime() and
strftime() C library functions that support
the -r marker format option. Configure tests
for the functions and defines this symbol.
HASSYSDC enables the use of a system-wide device
cache file and defines its path. See the
00DCACHE file of the lsof distribution for
more information on the system-wide device
cache file path option.
HAS_SYS_PIPEH indicates the dialect has a <sys/pipe.h>
header file.
HAS_SYS_SX_H indicates the FreeBSD 7.0 and higher system has
a <sys/sx.h> header file.
HASTAGTOPATH indicates the DEC OSF/1, Digital UNIX, or
Tru64 UNIX dialect has a libmsfs.so,
containing tag_to_path().
HASTMPNODE enables/disables readtnode() in node.c.
HASTCPOPT indicates the dialect has TCP option
information (i.e., from TF_* symbols)
available.
HASTCPTPIQ is defined when the dialect can duplicate
the receive and send queue sizes reported
by netstat.
HASTCPTPIW is defined when the dialect can duplicate
the receive and send window sizes reported
by netstat.
HASTCPUDPSTATE is defined when the dialect has support for
TCP and UDP state, including the "-s p:s"
option and associated speed ehancements.
HASTFS indicates that the Pyramid dialect has TFS
file system support.
HAS_UFS1_2 indicates the FreeBSD 6 and higher system has
UFS1 and UFS2 members in its inode structure.
HAS_UM_UFS indicates the OpenBSD version has UM_UFS[12]
definitions.
HASUNMINSOCK indicates the Linux version has a user name
element in the socket structure; a value of
0 says there is no unix_address member; 1,
there is.
HASUINT16TYPE is defined when the dialect has a typedef
for u_int16 that may conflict with some other
header file's redefinition (e.g., <afs/std.h>).
HASUTMPX indicates the dialect has a <utmpx.h> header
file.
HAS_UVM_INCL indicates the NetBSD or OpenBSD dialect has
a <uvm> include directory.
HAS_UW_CFS indicates the UnixWare 7.1.1 or above dialect
has CFS file system support.
HAS_UW_NSC indicates the UnixWare 7.1.1 or above dialect
has a NonStop Cluster (NSC) kernel.
HAS_V_LOCKF indicates the FreeBSD version has a v_lockf
member in the vode structure, defined in
<sys/vnode.h>.
HAS_VM_MEMATTR_T indicates the FreeBSD <sys/conf.h> uses the
vm_memattr_t typedef.
HASVMLOCKH indicates the FreeBSD dialect has <vm/lock.h>.
HASVNODE enables/disables readvnode() function in node.c.
HAS_V_PATH indicates the dialect's vnode structure has a
v_path member.
HAS_VSOCK indicates that the Solaris version has a VSOCK
member in the vtype enum
HASVXFS enables Veritas VxFS file system support for
the dialect. CAUTION, the dialect sources
must have the necessary support code.
HASVXFSDNLC indicates the VxFS file system has its own
name cache.
HASVXFS_FS_H indicates <sys/fs/vx_fs.h> exists.
HASVXFS_MACHDEP_H indicates <sys/fs/vx_machdep.h> exists.
HASVXFS_OFF64_T indicates <sys/fs/vx_solaris.h> exists and
has an off64_t typedef.
HASXVFSRNL indicates the dialect has VxFS Reverse Name
Lookup (RNL) support.
HASVXFS_SOL_H indicates <sys/fs/vx_sol.h> exists.
HASVXFS_SOLARIS_H indicates <sys/fs/vx_solaris.h> exists.
HASVXFS_U64_T if HASVXFS_SOLARIS_H is defined, this
variable indicates that <sys/fs/vx_solaris.h>
has a vx_u64_t typedef.
HASVXFSUTIL indicates the Solaris dialect has VxFS 3.4
or higher and has the utility libraries,
libvxfsutil.a (32 bit) and libvxfsutil64.a
(64 bit).
HASVXFS_VX_INODE indicates that <sys/fs/vx_inode.h> contains
a vx_inode structure.
HASWIDECHAR indicates the dialect has the wide-character
support functions iswprint(), mblen() and mbtowc().
HASXNAMNODE indicates the OSR dialect has <sys/fs/xnamnode.h>.
HASXOPT defines help text for dialect-specific X option
and enables X option processing in usage.c and
main.c.
HASXOPT_ROOT when defined, restricts the dialect-specific
X option to processes whose real user ID
is root.
HAS_ZFS indicates the dialect has support for the ZFS file
system.
HASXOPT_VALUE defines the default binary value for the X option
in store.c.
HASZONES the Solaris dialect has zones.
HAVECLONEMAJ defines the name of the status variable
that indicates a clone major device number
is available in CLONEMAJ. (Also see CLONEMAJ
and HAS_STD_CLONE.)
HPUX_KERNBITS defines the number of bits in the HP-UX 10.30
and above kernel "basic" word: 32 or 64.
KA_T defines the type cast required to assign
space to kernel pointers. When not defined
by a dialect header file, KA_T defaults to
unsigned long.
KA_T_FMT_X defines the printf format for printing a
KA_T -- the default is "%#lx" for the
default unsigned long KA_T cast.
LSOF_ARCH See 00XCONFIG.
LSOF_BLDCMT See 00XCONFIG.
LSOF_CC See 00XCONFIG.
LSOF_CCV See 00XCONFIG.
LSOF_HOST See 00XCONFIG.
LSOF_INCLUDE See 00XCONFIG.
LSOF_LOGNAME See 00XCONFIG.
LSOF_MKC See the "The Mksrc Shell Script" section of
this file.
LSOF_SYSINFO See 00XCONFIG.
LSOF_USER See 00XCONFIG.
LSOF_VERS See 00XCONFIG.
LSOF_VSTR See 00XCONFIG.
MACH defines a MACH system.
N_UNIXV defines an alternate value for the N_UNIV symbol.
NCACHELDPFX defines C code to be executed before calling
ncache_load().
NCACHELDSFX defines C code to be executed after calling
ncache_load().
NEEDS_BOOLEAN_T indicates the FreeBSD 9 and above system needs a
boolean_t definition for <sys/conf.h>.
NEVER_HASDCACHE keeps the Customize script from offering to
change HASDCACHE by its presence anywhere
in a dialect's machine.h header file --
e.g., in a comment. See the Customize
script or machine.h in dialects/linux/proc.
NEVER_WARNDEVACCESS keeps the Customize script from offering to
change WARNDEVACCESS by its presence anywhere
in a dialect's machine.h header file --
including in a comment. See the Customize
script or machine.h in dialects/linux/proc.
NLIST_TYPE is the type of the nlist table, Nl[], if it is
not nlist. HASNLIST must be set for this
definition to be effective.
NOWARNBLKDEV specifies that no warning is to be issued
when no block devices are found. This
definiton is used only when HASBLKDEV is
also defined.
OFFDECDIG specifies how many decimal digits will be
printed for the file offset in a 0t form
before switching to a 0x form. The count
includes the "0t". A count of zero means
the size is unlimited.
PRIVFILETYPE is the number of a private file type, found
in the f_type member of the file struct, to
be processed by the HASPRIVFILETYPE function.
See the AIX dialect sources for an example.
_PSTAT_STREAM_GET_XPORT
indicates the HP-UX PSTAT header files require
this symbol to be defined for proper handling of
stream export data.
TIMEVAL_LSOF defines the name of the timeval structure.
The default is timeval. /dev/kmem-based
Linux lsof redefines timeval with this
symbol to avoid conflicts between glibc
and kernel definitions.
TYPELOGSECSHIFT defines the type of the cdfs_LogSecShift
member of the cdfs structure for UnixWare
7 and higher.
UID_ARG_T defines the cast on a User ID when passed
as a function argument.
USE_LIB_COMPLETEVFS
selects the use of the completevfs() function
in lsof4/lib/cvfs.c.
USE_LIB_FIND_CH_INO
selects the use of the find_ch_ino() inode
function in lsof4/lib/fino.c.
Note: HASBLKDEV selects the has_bl_ino()
function.
USE_LIB_IS_FILE_NAMED
selects the use of the is_file_named() function
in lsof4/lib/isfn.c.
USE_LIB_LKUPDEV selects the use of the lkupdev() function
in lsof4/lib/lkud.c.
Note: HASBLKDEV selects the lkupbdev() function.
USE_LIB_PRINTDEVNAME
selects the use of the printdevname() function
in lsof4/lib/pdvn.c.
Note: HASBLKDEV selects the printbdevname()
function.
USE_LIB_PRINT_TCPTPI
selects the use of the print_tcptpi() function
in lsof4/lib/ptti.c.
USE_LIB_PROCESS_FILE
selects the use of the process_file() function
in lsof4/lib/prfp.c.
USE_LIB_READDEV selects the use of the readdev() and stkdir()
functions in lsof4/lib/rdev.c.
USE_LIB_READMNT selects the use of the readmnt() function
in lsof4/lib/rmnt.c.
USE_LIB_RNAM selects the use of the device cache functions
in lsof4/lib/rnam.c.
Note: HASNCACHE must also be defined.
USE_LIB_RNCH selects the use of the device cache functions
in lsof4/lib/rnch.c.
Note: HASNCACHE must also be defined.
USE_STAT is defined for those dialects that must
use the stat(2) function instead of lstat(2)
to scan /dev -- i.e., in the readdev()
function.
VNODE_VFLAG is an alternate name for the vnode structure's
v_flag member.
WARNDEVACCESS enables the issuing of a warning message when
lsof is unable to access /dev (or /device)
or one of its subdirectories, or stat(2)
a file in them. Some dialects (e.g., HP-UX)
have many inaccessible subdirectories and
it is appropriate to inhibit the warning
for them with WARNDEVACCESS. The -w option
will also inhibit these warnings.
WARNINGSTATE when defined, disables the default issuing
of warning messages. WARNINGSTATE is
undefined by default for all dialects in
the lsof distribution.
WIDECHARINCL defines the header file to be included (if any)
when wide-character support is enabled with
HASWIDECHAR.
zeromem() defines a macro to zero memory -- e.g., using
bzero() or memset().
Any dialect's machine.h file and Configure stanza can serve as a
template for building your own. All machine.h files usually have
all definitions, disabling some (with comment prefix and suffix)
and enabling others.
Options: Common and Special
---------------------------
All but one lsof option is common; the specific option is ``-X''.
If a dialect does not support a common option, the related #define
in machine.h -- e.g., HASCOPT -- should be deselected.
The specific option, ``-X'', may be used by any dialect for its
own purpose. Right now (May 30, 1995) the ``-X'' option is binary
(i.e., it's not allowed arguments of its own, and its value must
be 0 or 1) but that could be changed should the need arise. The
option is enabled with the HASXOPT definition in machine.h; its
default value is defined by HASXOPT_VALUE.
The value of HASXOPT should be the text displayed for ``-X'' by
the usage() function in usage.c. HASXOPT_VALUE should be the
default value, 0 or 1.
AIX for the IBM RICS System/6000 defines the ``-X'' option to
control readx() usage, since there is a bug in AIX kernels that
readx() can expose for other processes.
Defining Dialect-Specific Symbols and Global Storage
----------------------------------------------------
A dialect's dlsof.h and dstore.c files contain dialect-specific
symbol and global storage definitions. There are symbol definitions,
for example, for function and data casts, and for file paths.
Dslof.h defines lookup names the nlist() table -- X_* symbols --
when nlist() is being used.
Global storage definitions include such things as structures for
local Virtual File System (vfs) information; mount information;
search file information; and kernel memory file descriptors --
e.g., Kmem for /dev/kmem, Mem for /dev/mem, Swap for /dev/drum.
Coding Dialect-specific Functions
---------------------------------
Each supported dialect must have some basic functions that the
common functions of the top level may call. Some of them may be
obtained from the library in lsof4/lib, selected and customized by
#define's in the dialect machine.h header file. Others may have
to be coded specifically for the dialect.
Each supported dialect usually has private functions, too. Those
are wholly determined by the needs of the dialect's data organization
and access.
These are some of the basic functions that each dialect must supply
-- they're all defined in proto.h:
initialize() function to initialize the dialect
is_file_named() function to check if a file was named
by an optional file name argument
(lsof4/lib/isfn.c)
gather_proc_info() function to gather process table
and related information and cache it
printchdevname() function to locate and optionally
print the name of a character device
(lsof4/lib/pdvn.c)
print_tcptpistate() function to print the TCP or TPI
state for a TCP or UDP socket file,
if the one in lib/ptti.c isn't
suitable (define USE_LIB_PRINT_TCPTPI
to activate lib/ptti.c)
process_file() function to process an open file
structure (lsof4/lib/prfp.c)
process_node() function to process a primary node
process_socket() function to process a socket
readdev() and stkdir() functions to read and cache device
information (lsof4/lib/rdev.c)
readmnt() function to read mount table information
(lsof4/lib/rmnt.c)
Other common functions may be needed, and might be obtained from
lsof4/lib, depending on the needs of the dialect's node and socket
file processing functions.
Check the functions in lsof4/lib and specific lsof4/dialects/*
files for examples.
As you build these functions you will probably have to add #include's
to dlsof.h.
Function Prototype Definitions and the _PROTOTYPE Macro
-------------------------------------------------------
Once you've defined your dialect-specific definitions, you should
define their prototypes in dproto.h or locally in the file where
they occur and are used. Do this even if your compiler is not ANSI
compliant -- the _PROTOTYPE macro knows how to cope with that and
will avoid creating prototypes that will confuse your compiler.
The Makefile
------------
Here are some general rules for constructing the dialect Makefile.
* Use an existing dialect's Makefile as a template.
* Make sure the echo actions of the install rule are appropriate.
* Use the DEBUG string to set debugging options, like ``-g''.
You may also need to use the -O option when forking and
SIGCHLD signals defeat your debugger.
* Don't put ``\"'' in a compiler flags -D<symbol>=<string>
clause in your Makefile. Leave off the ``\"'' even though
you want <string> to be a string literal and instead adapt
the N_UNIX* macros you'll find in Makefiles for FreeBSD
and Linux. That will allow the Makefile's version.h rule
to put CFLAGS into version.h without having to worry about
the ``\"'' sequences.
* Finally, remember that strings can be passed from the top
level's Configure shell script. That's an appropriate way
to handle options, especially if there are multiple versions
of the Unix dialect to which you are porting lsof 4.
The Mksrc Shell Script
----------------------
Pattern your Mksrc shell script after an existing one from another
dialect. Change the D shell variable to the name of your dialect's
subdirectory in lsof4/dialects. Adjust any other shell variable
to your local conditions. (Probably that won't be necessary.)
Note that, if using symbolic links from the top level to your
dialect subdirectory is impossible or impractical, you can set the
LSOF_MKC shell variable in Configure to something other than
"ln -s" -- e.g., "cp," and Configure will pass it to the Mksrc
shell script in the M environment variable.
The MkKernOpts Shell Script
---------------------------
The MkKernOptrs shell script is used by some dialects -- e.g.,
Pyramid DC/OSx and Reliant UNIX -- to determine the compile-time
options used to build the current kernel that affect kernel structure
definitions, so those same options can be used to build lsof.
Configure calls MkKernOpts for the selected dialects.
If your kernel is built with options that affect structure definitions.
-- most commonly affected are the proc structure from <sys/proc.h>
and the user structure from <sys/user.h> -- check the MkKernOpts
in lsof4/dialects/irix for a comprehensive example.
Testing and the Lsof Test Suite
-------------------------------
Once you have managed to create a port, here are some tips for
testing it.
* First look at the test suite in the tests/ sub-directory of the
lsof distribution. While it will need to be customized to be
usable with a new port, it should provide ideas on things to
test. Look for more information about the test suite in the
00TEST file.
* Pick a simple process whose open files you are likely to
know and see if the lsof output agrees with what you know.
(Hint: select the process with `lsof -p <process_PID>`.)
Are the device numbers and device names correct?
Are the file system names and mount points correct?
Are inode numbers and sizes correct?
Are command names, file descriptor numbers, UIDs, PIDs, PGIDs,
and PPIDs correct?
A simple tool that does a stat(2) of the files being examined
and reports the stat struct contents can provide a reference for
some values; so can `ls -l /dev/<device>`.
* Let lsof list information about all open files and ask the
same questions. Look also for error messages about not being
able to read a node or structure.
* Pick a file that you know is open -- open it and hold it
that way with a C program (not vi), if you must. Ask lsof to
find the file's open instance by specifying its path to lsof.
* Create a C program that opens a large number of files and holds
them open. Background the test process and ask lsof to list
its files.
* Generate some locks -- you may need to write a C program to
do this, hold the locked file open, and see if lsof can identify
the lock properly. You may need to write several C programs
if your dialect supports different lock functions -- fnctl(),
flock(), lockf(), locking().
* Identify a process with known Internet file usage -- inetd
is a good one -- and ask lsof to list its open files. See if
protocols and service names are listed properly.
See if your lsof identifies Internet socket files properly for
rlogind or telnetd processes.
* Create a UNIX domain socket file, if your dialect allows it,
hold it open by backgrounding the process, and see if lsof can
identify the open UNIX domain socket file properly.
* Create a FIFO file and see what lsof says about it.
* Watch an open pipe -- `lsof -u <your_login> | less` is a
good way to do this.
* See if lsof can identify NFS files and their devices properly.
Open and hold open an NFS file and see if lsof can find the open
instance by path.
* If your test system has CD-ROM and floppy disk devices, open
files on them and see if lsof reports their information correctly.
Such devices often have special kernel structures associated
with them and need special attention from lsof for their
identification. Pay particular attention to the inode numbers
lsof reports for CD-ROM and floppy disk files -- often they are
calculated dynamically, rather than stored in a kernel node
structure.
* If your implementation can probe the kernel name cache, look
at some processes with open files whose paths you know to see
if lsof identifies any name components. If it doesn't, make
sure the name components are in the name cache by accessing
the files yourself with ls or a similar tool.
* If your dialect supports the /proc file system, use a C program
to open files there, background a test process, and ask lsof to
report its open files.
* If your dialect supports fattach(), create a small test program
to use it, background a test process, and ask lsof to report
its open files.
I can supply some quick-and-dirty tools for reporting stat buffer
contents, holding files open, creating UNIX domain files, creating
FIFOs, etc., if you need them.
Where Next?
-----------
Is this document complete? Certainly not! One might wish that it
were accompanied by man pages for all lsof functions, by free beer
or chocolates, by ... (You get the idea.)
But those things are not likely to happen as long as lsof is a
privately supported, one man operation.
So, if you need more information on how lsof is constructed or
works in order to do a port of your own, you'll have to read the
lsof source code. You can also ask me questions via email, but
keep in mind the private, one-man nature of current lsof support.
Vic Abell <abe@purdue.edu>
September 27, 2011