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/ 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//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 / 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 / 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//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) in preference to #ifdef 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., -- 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 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 references vop_advlock_args. HAS_AFS enables AFS support code for the dialect. HAS_ATOMIC_T indicates the Linux version has an 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 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 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 header file available. HASCWDINFO indicates the cwdinfo structure is defined in the NetBSD . 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 . 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 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 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 . HAS_FL_FILE indicates the Linux version has an fl_file element in the lock structure of . HAS_FL_WHENCE indicates the Linux version has an fl_whence element in the lock structure of . 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 . 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 or . 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., ). HASINT32TYPE is defined when the dialect has a typedef for int32 that may conflict with some other header file's redefinition (e.g., ). HASINTSIGNAL is defined when signal() returns an int. HAS_IPCLASSIFIER_H is defined for Solaris dialects that have the 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 . 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 . 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 . 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 and 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 header file. HAS_LWP_H is defined for BSD dialects that have the 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 . 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 header file has the pc_direntpersec() macro. HAS_PAD_MUTEX indicates the Solaris 11 system has the pad_mutex_t typedef in its 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 format; 1, 0x. 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 . 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 . 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 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 . HASSOUXSOUA indicates that the Solaris 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 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 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 header file. HAS_SYS_SX_H indicates the FreeBSD 7.0 and higher system has a 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., ). HASUTMPX indicates the dialect has a header file. HAS_UVM_INCL indicates the NetBSD or OpenBSD dialect has a 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 . HAS_VM_MEMATTR_T indicates the FreeBSD uses the vm_memattr_t typedef. HASVMLOCKH indicates the FreeBSD dialect has . 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 exists. HASVXFS_MACHDEP_H indicates exists. HASVXFS_OFF64_T indicates exists and has an off64_t typedef. HASXVFSRNL indicates the dialect has VxFS Reverse Name Lookup (RNL) support. HASVXFS_SOL_H indicates exists. HASVXFS_SOLARIS_H indicates exists. HASVXFS_U64_T if HASVXFS_SOLARIS_H is defined, this variable indicates that 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 contains a vx_inode structure. HASWCTYPE_H indicates the FreeBSD version has wide-character support and the header file. Note: the HASWIDECHAR #define will also be set. HASWIDECHAR indicates the dialect has the wide-character support functions iswprint(), mblen() and mbtowc(). HASXNAMNODE indicates the OSR dialect has . 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 . NEEDS_MACH_PORT_T is defined for Darwin versions that need the inclusion of the header file . 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. SAVE_MP_IN_SFILE indicates the dialect needs to have the mounts structure pointer for a file system search argument recorded in the dialect's sfile structure. This definition is made in the dialect's dlsof.h header file within the sfile structure. 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= clause in your Makefile. Leave off the ``\"'' even though you want 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 and the user structure from -- 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 `.) 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/`. * 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 | 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 January 2, 2013