summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/00README102
-rwxr-xr-xtests/Add2TestDB83
-rwxr-xr-xtests/CkTestDB136
-rw-r--r--tests/LTbasic.c445
-rw-r--r--tests/LTbigf.c767
-rw-r--r--tests/LTdnlc.c426
-rw-r--r--tests/LTlib.c1104
-rw-r--r--tests/LTlock.c769
-rw-r--r--tests/LTnfs.c531
-rw-r--r--tests/LTnlink.c586
-rw-r--r--tests/LTsock.c886
-rw-r--r--tests/LTszoff.c509
-rw-r--r--tests/LTunix.c364
-rw-r--r--tests/LsofTest.h360
-rw-r--r--tests/Makefile159
-rw-r--r--tests/TestDB134
16 files changed, 7361 insertions, 0 deletions
diff --git a/tests/00README b/tests/00README
new file mode 100644
index 0000000..eee8e4d
--- /dev/null
+++ b/tests/00README
@@ -0,0 +1,102 @@
+
+ .../lsof_<version>/tests
+
+This sub-directory contains support for lsof's test suite. Find
+more information about the test suite in the 00TESTS file of the
+lsof distribution, which should be in in the parent of this
+subdirectory.
+
+These tests can be activated from .. with:
+
+ $ make test
+
+They can be activated from this directory with:
+
+ $ make
+ $ make test
+ $ make all
+
+These tests are all written in C, so individual tests may be
+activated by executing them directly -- e.g.,
+
+ $ ./LTlock
+
+It may sometimes be necessary to use execution-time options
+alter test behavior. (Some tests will suggest that when they
+encounter certain kinds of errors.) See the 00FAQ and 00TEST files
+in .. for more information.
+
+These tests check lsof field output, not lsof text output. There
+are no tests for lsof text output.
+
+Here is a brief description of the files in this subdirectory:
+
+ 00README this file
+
+ Add2TestDB a script to add the identity of the current
+ test to TestDB
+
+ CkTestDB a script to check the identity of this
+ dialect against the TestDB file
+
+ config.cc a file prepared by ../Configure that contains
+ the name (and possibly the path) to the C
+ compiler for the programs of this sub-directory
+
+ config.cflags a file prepared by ../Configure that contains
+ C compiler flags for the programs of this
+ sub-directory
+
+ config.libs a file prepared by ../Configure that contains
+ library load specifications -- i.e, make(1)
+ LDFLAGS
+
+ config.xobj a file prepared by ../Configure that contains
+ paths to any extra object files (*.o) needed
+ by the C programs in this directory
+
+ LsofTest.h lsof test definitions for C programs
+
+ LTbasic.c C source to basic lsof tests
+
+ LTbigf.c C source to a program that tests large file
+ sizes and offsets on dialects that support
+ file sizes > 32 bits
+
+ LTdnlc.c C source to a program that tests the
+ effectiveness of assembling path names from
+ the kernel's Dynamic Name Lookup Cache
+ (DNLC)
+
+ LTlib.c a support library in C
+
+ LTlock.c C source to a program that tests lock reporting
+
+ LTnfs C source to a program that tests for open NFS
+ files
+
+ LTnlink.c C source to a program that tests lsof's
+ reporting of open file link counts
+
+ LTsock.c C source to program that tests the finding
+ of IPv4 sockets
+
+ LTszoff.c C source to a program that tests file sizes
+ and offsets -- see LTbigf.c for a large
+ file (size > 32 bits) test
+
+ LTunix.c C source to a program that tests the finding
+ of UNIX domain sockets
+
+ Makefile the make(1) control file
+
+ The Makefile clean rule will not remove
+ config.* files, but the spotless rule will.
+ One the spotless rule has been used,
+ ../Configure must be re-run.
+
+ TestDB a data base of dialects where the test
+ suite has been validated
+
+Vic Abell
+April 11, 2002
diff --git a/tests/Add2TestDB b/tests/Add2TestDB
new file mode 100755
index 0000000..48603f9
--- /dev/null
+++ b/tests/Add2TestDB
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# Add2TestDB -- add the current test to the lsof test suite DB
+#
+# This script saves the current TestDB file in TestDB.old and adds
+# the words in config.cflags to it. "-D" prefixes on the words are
+# removed, the words are sorted, and they are joint in a single
+# line that is catenated to TestDB if it isn't already there.
+#
+# $Id: Add2TestDB,v 1.2 2002/04/19 11:53:37 abe Exp $
+
+# Check for config.flags.
+
+if test ! -r config.cflags
+then
+ echo "$0: no ./config.cflags file"
+ exit 1
+fi
+
+# Check for a current data base file.
+
+if test ! -r TestDB
+then
+ echo "$0: no ./TestDB file"
+ exit 1
+fi
+
+# Form a new data base line.
+
+new=""
+for i in `sort < config.cflags`
+do
+ w=`echo $i | sed 's/^-D//'`
+ if test "X$new" = "X"
+ then
+ new=$w
+ else
+ new="$new $w"
+ fi
+done
+
+# See if the new line is already in the data base.
+
+grep "$new" TestDB > /dev/null 2>&1
+if test $? -eq 0
+then
+ echo "\"$new\" is already in TestDB."
+ exit 1
+fi
+
+# Build a new data base file.
+
+if test ! -w TestDB
+then
+ echo "$0: can't write the following to the end of TestDB:"
+ echo " \"$new\""
+ exit 1
+fi
+rm -f TestDB.new
+cp TestDB TestDB.new
+chmod 644 TestDB.new
+echo "$new" >> TestDB.new
+
+# Archive the current data base file, if possible.
+
+if test -d OLD
+then
+ dt=`date`
+ dtm="========== $dt =========="
+ if test -r OLD/TestDB
+ then
+ echo "$dtm" >> OLD/TestDB
+ else
+ echo "$dtm" > OLD/TestDB
+ fi
+ cat TestDB >> OLD/TestDB
+fi
+
+# Put the new data base file in place.
+
+mv TestDB.new TestDB
+echo "\"$new\" added to TestDB."
+exit 0
diff --git a/tests/CkTestDB b/tests/CkTestDB
new file mode 100755
index 0000000..80bee63
--- /dev/null
+++ b/tests/CkTestDB
@@ -0,0 +1,136 @@
+#!/bin/sh
+#
+# CkTestDB -- see if this dialect is has been tested
+#
+# This script builds a line from config.flags in the form of lines in
+# ./TestDB, (See Add2TestDB.)
+#
+# It then compares the line to TestDB. If the line is found, the script
+# exits. if the line is not found, the script issues a warning and requests
+# a go-ahead confirmation.
+#
+# The script will exit 0 if the test line is in the DB or the go-ahead
+# confirmation is positive.
+#
+# $Id: CkTestDB,v 1.3 2010/01/18 19:02:21 abe Exp abe $
+
+# Check for config.flags.
+
+if test ! -r config.cflags
+then
+ echo "$0: no ./config.cflags file"
+ exit 1
+fi
+
+# Check for a current data base file.
+
+if test ! -r TestDB
+then
+ echo "$0: no ./TestDB file"
+ exit 1
+fi
+
+# Form a data base line.
+
+new=""
+for i in `LC_ALL=C sort < config.cflags`
+do
+ w=`echo $i | sed 's/^-D//'`
+ if test "X$new" = "X"
+ then
+ new=$w
+ else
+ new="$new $w"
+ fi
+done
+
+# See if the line is already in the data base. Exit with success (0), if it is.
+
+grep "^$new\$" TestDB > /dev/null 2>&1
+if test $? -eq 0
+then
+ exit 0
+fi
+
+# This dialect may never have been validated with the test suite.
+
+# If the standard input is not a TTY, quit, because no interaction
+# is possible.
+
+tty -s > /dev/null 2>&1
+if test $? -ne 0
+then
+ echo ""
+ echo "This suite has not been validated on:"
+ echo ""
+ echo " $new"
+ echo ""
+ exit 1
+fi
+
+# Establish trap and stty handling.
+
+ISIG=":"
+trap '$ISIG; exit 1' 1 2 3 15
+stty -a 2>&1 | grep isig > /dev/null
+if test $? -eq 0
+then
+ stty -a 2>&1 | egrep -e -isig > /dev/null
+ if test $? -eq 0
+ then
+ ISIG="stty -isig"
+ stty isig
+ fi
+fi
+
+# Establish echo type -- Berkeley or SYSV.
+
+j=`echo -n ""`
+if test "X$j" = "X-n "
+then
+ EC="\c"
+ EO=""
+else
+ EC=""
+ EO="-n"
+fi
+
+# Display a validation warning.
+
+cat << .CAT_MARK > /dev/tty
+
+==================================================================
+
+!!!WARNING!!!
+
+This dialect or its particular version may not have been validated
+with the lsof test suite. Consequently some tests may fail or may
+not even compile.
+
+This is the computed identity of this dialect, not found in the
+test data base file, ./TestDB:
+
+.CAT_MARK
+echo " $new" > /dev/tty
+END=0
+while test $END = 0
+do
+ echo "" > /dev/tty
+ echo $EO "Do you want to continue (y|n) [n]? $EC" > /dev/tty
+ read ANS EXCESS
+ if test "X$ANS" = "Xn" -o "X$ANS" = "XN"
+ then
+ exit 1
+ fi
+ if test "X$ANS" = "Xy" -o "X$ANS" = "XY"
+ then
+ exit 0
+ else
+ echo "Please answer y or n." > /dev/tty
+ fi
+done
+
+# Should never get here!
+
+echo "$0: unexpected failure!"
+exit 2
diff --git a/tests/LTbasic.c b/tests/LTbasic.c
new file mode 100644
index 0000000..ce04705
--- /dev/null
+++ b/tests/LTbasic.c
@@ -0,0 +1,445 @@
+/*
+ * LTbasic.c -- Lsof Test basic tests
+ *
+ * The basic tests measure the finding by lsof of its own open CWD, open
+ * executable (when possible), and open /dev/kmem files.
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+
+/*
+ * Local definitions
+ */
+
+
+/*
+ * Globals
+ */
+
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *tstlsof,(char **texec, char **tkmem, char **tproc));
+
+
+/*
+ * Main program for dialects that support locking tests.
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ char *em; /* error message pointer */
+ char *texec = (char *)NULL; /* lsof executable test result */
+ char *tkmem = (char *)NULL; /* /dev/kmem test result */
+ char *tproc = (char *)NULL; /* lsof process test result */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "h", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h]", Pn);
+ PrtMsgX (" -h print help (this panel)", Pn, cleanup,
+ xv);
+ }
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Test lsof.
+ */
+ if ((em = tstlsof(&texec, &tkmem, &tproc)))
+ PrtMsg(em, Pn);
+ if (texec)
+ PrtMsg(texec, Pn);
+ if (tkmem)
+ PrtMsg(tkmem, Pn);
+ if (tproc)
+ PrtMsg(tproc, Pn);
+/*
+ * Compute exit value and exit.
+ */
+ if (em || texec || tkmem || tproc) {
+ if (strcmp(LT_DEF_LSOF_PATH, LsofPath)) {
+ PrtMsg (" ", Pn);
+ PrtMsg ("Hint: you used the LT_LSOF_PATH environment variable to",
+ Pn);
+ PrtMsg (" specify this path to the lsof executable:\n", Pn);
+ (void) snprintf(buf, sizeof(buf) - 1, " %s\n", LsofPath);
+ buf[sizeof(buf) - 1] = '\0';
+ PrtMsg (buf, Pn);
+ PrtMsgX(" Make sure its revision is 4.63 or higher.",
+ Pn, cleanup, 1);
+ } else
+ PrtMsgX("", Pn, cleanup, 1);
+ }
+ (void) PrtMsgX("OK", Pn, cleanup, 0);
+ return(0);
+}
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+}
+
+
+/*
+ * tstlsof() -- test for the lsof process
+ */
+
+static char *
+tstlsof(texec, tkmem, tproc)
+ char **texec; /* result of the executable test */
+ char **tkmem; /* result of the /dev/kmem test */
+ char **tproc; /* result of the lsof process test */
+{
+ char buf[2048]; /* temporary buffer */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTdev_t cwddc; /* CWD device components */
+ struct stat cwdsb; /* CWD stat(2) buffer */
+ LTfldo_t *devp; /* device pointer */
+ int execs = 0; /* executable status */
+ int fdn; /* FD is a number */
+ LTfldo_t *fdp; /* file descriptor pointer */
+ LTfldo_t *fop; /* field output pointer */
+ char ibuf[64]; /* inode string buffer */
+ LTfldo_t *inop; /* inode number pointer */
+ LTdev_t kmemdc; /* /dev/kmem device components */
+ int kmems = 0; /* kmem status */
+ struct stat kmemsb; /* /dev/kmem stat(2) buffer */
+ LTdev_t lsofdc; /* lsof device components */
+ struct stat lsofsb; /* lsof stat(2) buffer */
+ int nf; /* number of fields */
+ char *opv[4]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ int procs = 0; /* process status */
+ LTfldo_t *rdevp; /* raw device pointer */
+ char *tcp; /* temporary character pointer */
+ int ti; /* temporary integer */
+ LTdev_t tmpdc; /* temporary device components */
+ LTfldo_t *typ; /* file type pointer */
+ int xwhile; /* exit while() flag */
+
+/*
+ * Get lsof executable's stat(2) information.
+ */
+ if (stat(LsofPath, &lsofsb)) {
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! stat(%s): %s",
+ LsofPath, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ execs = 1;
+ } else if ((cem = ConvStatDev(&lsofsb.st_dev, &lsofdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ execs = 1;
+ }
+
+#if defined(LT_KMEM)
+/*
+ * Get /dev/kmem's stat(2) information.
+ */
+ if (stat("/dev/kmem", &kmemsb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't stat(2) /dev/kmem: %s", strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ kmems = 1;
+ } else if ((cem = ConvStatDev(&kmemsb.st_rdev, &kmemdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ kmems = 1;
+ }
+#else /* !defined(LT_KMEM) */
+ kmems = 1;
+#endif /* defined(LT_KMEM) */
+
+/*
+ * Get CWD's stat(2) information.
+ */
+ if (stat(".", &cwdsb)) {
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! stat(.): %s",
+ strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ procs = 1;
+ } else if ((cem = ConvStatDev(&cwdsb.st_dev, &cwddc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ procs = 1;
+ }
+
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ ti = 0;
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+#if defined(USE_LSOF_X_OPT)
+ opv[ti++] = "-X";
+#endif /* defined(USE_LSOF_X_OPT) */
+
+ opv[ti++] = "-clsof";
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+/*
+ * Read lsof output.
+ */
+ xwhile = execs + kmems + procs;
+ while ((xwhile < 3) && (fop = RdFrLsof(&nf, &cem))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != LsofPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Scan its fields.
+ */
+ if (!pids)
+ break;
+ devp = inop = rdevp = typ = (LTfldo_t *)NULL;
+ fdp = fop;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch(fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_RDEV:
+ rdevp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * A file descriptor line has been processes.
+ *
+ * Set the descriptor's numeric status.
+ *
+ * Check descriptor by FD type.
+ */
+
+ for (fdn = 0, tcp = fdp->v; *tcp; tcp++) {
+ if (!isdigit((unsigned char)*tcp)) {
+ fdn = -1;
+ break;
+ }
+ fdn = (fdn * 10) + (int)(*tcp - '0');
+ }
+ if (!procs
+ && (fdn == -1)
+ && !strcasecmp(fdp->v, "cwd")
+ && typ
+ && (!strcasecmp(typ->v, "DIR") || !strcasecmp(typ->v, "VDIR"))
+ ) {
+
+ /*
+ * This is the CWD for the process. Make sure its information
+ * matches what stat(2) said about the CWD.
+ */
+ if (!devp || !inop)
+ break;
+ if ((cem = ConvLsofDev(devp->v, &tmpdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u",
+ (unsigned int)cwdsb.st_ino);
+ ibuf[sizeof(ibuf) - 1] = '\0';
+ if ((tmpdc.maj == cwddc.maj)
+ && (tmpdc.min == cwddc.min)
+ && (tmpdc.unit == cwddc.unit)
+ && !strcmp(inop->v, ibuf)
+ ) {
+ procs = 1;
+ xwhile++;
+ }
+ break;
+ }
+ if (!kmems
+ && (fdn >= 0)
+ && typ
+ && (!strcasecmp(typ->v, "CHR") || !strcasecmp(typ->v, "VCHR"))
+ ) {
+
+ /*
+ * /dev/kmem hasn't been found and this is an open character device
+ * file with a numeric descriptor.
+ *
+ * See if it is /dev/kmem.
+ */
+ if (!inop || !rdevp)
+ break;
+ if ((cem = ConvLsofDev(rdevp->v, &tmpdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u",
+ (unsigned int)kmemsb.st_ino);
+ ibuf[sizeof(ibuf) - 1] = '\0';
+ if ((tmpdc.maj == kmemdc.maj)
+ && (tmpdc.min == kmemdc.min)
+ && (tmpdc.unit == kmemdc.unit)
+ && !strcmp(inop->v, ibuf)
+ ) {
+ kmems = 1;
+ xwhile++;
+ }
+ break;
+ }
+ if (!execs
+ && (fdn == -1)
+ && typ
+ && (!strcasecmp(typ->v, "REG") || !strcasecmp(typ->v, "VREG"))
+ ) {
+
+ /*
+ * If this is a regular file with a non-numeric FD, it may be the
+ * executable.
+ */
+ if (!devp || !inop)
+ break;
+ if ((cem = ConvLsofDev(devp->v, &lsofdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u",
+ (unsigned int)lsofsb.st_ino);
+ ibuf[sizeof(ibuf) - 1] = '\0';
+ if ((tmpdc.maj == lsofdc.maj)
+ && (tmpdc.min == lsofdc.min)
+ && (tmpdc.unit == lsofdc.unit)
+ && !strcmp(inop->v, ibuf)
+ ) {
+ execs = 1;
+ xwhile++;
+ }
+ }
+ }
+ }
+ (void) StopLsof();
+ if (!execs)
+ *texec = "ERROR!!! open lsof executable wasn't found.";
+ if (!kmems)
+ *tkmem = "ERROR!!! open lsof /dev/kmem usage wasn't found.";
+ if (!procs)
+ *tproc = "ERROR!!! lsof process wasn't found.";
+ return(pem);
+}
diff --git a/tests/LTbigf.c b/tests/LTbigf.c
new file mode 100644
index 0000000..ed740e7
--- /dev/null
+++ b/tests/LTbigf.c
@@ -0,0 +1,767 @@
+/*
+ * LTbigf.c -- Lsof Test big file size and offset tests
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+
+#if !defined(LT_BIGF)
+
+/*
+ * Here begins the version of this program for dialects that don't support
+ * large files.
+ */
+
+
+/*
+ * Main program for dialects that don't support large files
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char *pn; /* program name */
+/*
+ * Get program name and issue start and exit message.
+ */
+ if ((pn = (char *)strrchr(argv[0], '/')))
+ pn++;
+ else
+ pn = argv[0];
+
+ (void) printf("%s ... %s\n", pn, LT_DONT_DO_TEST);
+ return(0);
+}
+#else /* defined(LT_BIGF) */
+
+/*
+ * Here begins the version of this program for dialects that support
+ * large files.
+ */
+
+#include "lsof_fields.h"
+
+
+/*
+ * Pre-definitions that may be changed by specific dialects
+ */
+
+#define OFFTST_STAT 1 /* offset tests status */
+
+
+#if defined(LT_DIAL_aix)
+/*
+ * AIX-specific definitions
+ */
+
+#define OFFSET_T off64_t /* define offset type */
+#endif /* defined(LT_DIAL_aix) */
+
+
+#if defined(LT_DIAL_bsdi)
+/*
+ * BSDI-specific definitions
+ */
+
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+#endif /* defined(LT_DIAL_bsdi) */
+
+
+#if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific definitions
+ */
+
+# if LT_VERS>=900
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+# endif /* LT_VERS>=900 */
+#endif /* defined(LT_DIAL_darwin) */
+
+
+#if defined(LT_DIAL_du)
+/*
+ * DEC_OSF/1|Digital_UNIX|Tru64_UNIX-specific items
+ */
+
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+#endif /* defined(LT_DIAL_du) */
+
+
+#if defined(LT_DIAL_freebsd)
+/*
+ * FreeBSD-specific definitions
+ */
+
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+#endif /* defined(LT_DIAL_freebsd) */
+
+
+#if defined(LT_DIAL_linux)
+/*
+ * Linux-specific definitions
+ */
+
+#undef OFFTST_STAT
+#define OFFTST_STAT 0 /* Linux lsof may not be able to report
+ * offsets -- see the function
+ * ck_Linux_offset_support() */
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+
+_PROTOTYPE(static int ck_Linux_offset_support,(void));
+#endif /* defined(LT_DIAL_linux) */
+
+
+#if defined(LT_DIAL_hpux)
+/*
+ * HP-UX-specific definitions
+ */
+
+#define OFFSET_T off64_t /* define offset type */
+#endif /* defined(LT_DIAL_hpux) */
+
+
+#if defined(LT_DIAL_netbsd)
+/*
+ * NetBSD-specific definitions
+ */
+
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+#endif /* defined(LT_DIAL_netbsd) */
+
+
+#if defined(LT_DIAL_openbsd)
+/*
+ * OpenBSD-specific definitions
+ */
+
+#define OFFSET_T off_t /* define offset type */
+#define OPENF open /* define open function */
+#define SEEKF lseek /* define seek function */
+#define STATF stat /* define stat function */
+#define STATS struct stat /* define stat structure */
+#endif /* defined(LT_DIAL_openbsd) */
+
+
+#if defined(LT_DIAL_ou)
+/*
+ * OpenUNIX-specific items
+ */
+
+#include <signal.h>
+
+#define IGNORE_SIGXFSZ
+#define OFFSET_T off64_t /* define offset type */
+#endif /* defined(LT_DIAL_ou) */
+
+
+#if defined(LT_DIAL_solaris)
+/*
+ * Solaris-specific definitions
+ */
+
+#define OFFSET_T off64_t /* define offset type */
+#endif /* defined(LT_DIAL_solaris) */
+
+
+#if defined(LT_DIAL_uw)
+/*
+ * UnixWare-specific items
+ */
+
+#include <signal.h>
+
+#define IGNORE_SIGXFSZ
+#define OFFSET_T off64_t /* define offset type */
+#endif /* defined(LT_DIAL_uw) */
+
+
+/*
+ * Local definitions
+ */
+
+#if !defined(OPENF)
+#define OPENF open64 /* open() function */
+#endif /* !defined(OPENF) */
+
+#if !defined(OFFSET_T)
+#define OFFSET_T unsigned long long /* offset type */
+#endif /* !defined(OFFSET_T) */
+
+#if !defined(SEEKF)
+#define SEEKF lseek64 /* seek() function */
+# endif /* !defined(SEEKF) */
+
+#if !defined(STATF)
+#define STATF stat64 /* stat(2) structure */
+#endif /* !defined(STATF) */
+
+#if !defined(STATS)
+#define STATS struct stat64 /* stat(2) structure */
+#endif /* !defined(STATS) */
+
+#define TST_OFFT 0 /* test offset in 0t decimal*/
+#define TST_OFFX 1 /* test offset in hex */
+#define TST_SZ 2 /* test size */
+
+
+/*
+ * Globals
+ */
+
+int Fd = -1; /* test file descriptor; open if >= 0 */
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Path = (char *)NULL; /* test file path; none if NULL */
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static int tstwlsof,(int tt, char *opt, OFFSET_T sz));
+
+
+/*
+ * Main program for dialects that support large files
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ int do_offt = OFFTST_STAT; /* do offset tests if == 1 */
+ char *em; /* error message pointer */
+ int i; /* temporary integer */
+ int len; /* string length */
+ OFFSET_T sz = 0x140000000ll; /* test file size */
+ char szbuf[64]; /* size buffer */
+ char *tcp; /* temporary character pointer */
+ int tofft = 0; /* 0t offset test result */
+ int toffx = 0; /* 0x offset test result */
+ int tsz = 0; /* size test result */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "hp:", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h] [-p path]", Pn);
+ PrtMsg (" -h print help (this panel)", Pn);
+ PrtMsgX (" -p path define test file path", Pn, cleanup, xv);
+ }
+
+#if defined(LT_DIAL_linux)
+/*
+ * If this is Linux, see if lsof can report file offsets.
+ */
+ do_offt = ck_Linux_offset_support();
+#endif /* defined(LT_DIAL_linux) */
+
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Construct the path. If LT_BIGSZOFF_PATH is defined in the environment,
+ * use it. otherwise construct a path in the CWD.
+ */
+ if (!(Path = LTopt_p)) {
+ (void) snprintf(buf, sizeof(buf), "./config.LTbigf%ld",
+ (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ Path = MkStrCpy(buf, &len);
+ }
+/*
+ * Fill buffer for writing to the test file.
+ */
+ for (i = 0; i < sizeof(buf); i++) {
+ buf[i] = (char)(i & 0xff);
+ }
+
+#if defined(IGNORE_SIGXFSZ)
+/*
+ * Ignore SIGXFSZ, if directed by a dialect-specific option.
+ */
+ (void) signal(SIGXFSZ, SIG_IGN);
+#endif /* defined(IGNORE_SIGXFSZ) */
+
+/*
+ * Open a new test file at the specified path.
+ */
+ (void) unlink(Path);
+ if ((Fd = OPENF(Path, O_RDWR|O_CREAT, 0600)) < 0) {
+ (void) fprintf(stderr, "ERROR!!! can't open %s\n", Path);
+
+print_hint:
+
+ /*
+ * Print a hint about the LT_BIGSZOFF_PATH environment variable.
+ */
+
+ MsgStat = 1;
+ (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s",
+ errno, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) PrtMsg("Hint: try using \"-p path\" to supply a path in a", Pn);
+ (void) PrtMsg("file system that has large file support enabled.\n", Pn);
+ (void) PrtMsg("Hint: try raising the process ulimit file block", Pn);
+ (void) PrtMsg("size to a value that will permit this test to", Pn);
+ (void) snprintf(szbuf, sizeof(szbuf) - 1, "%lld", (long long)sz);
+ szbuf[sizeof(szbuf) - 1] = '\0';
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "write a file whose size appears to be %s", szbuf);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) PrtMsg("bytes. (The file really isn't that big -- it", Pn);
+ (void) PrtMsg("just has a large \"hole\" in its mid-section.)\n", Pn);
+ (void) PrtMsgX("See 00FAQ and 00TEST for more information.", Pn,
+ cleanup, 1);
+ }
+/*
+ * Write a buffer load at the beginning of the file.
+ */
+ if (SEEKF(Fd, (OFFSET_T)0, SEEK_SET) < 0) {
+ (void) fprintf(stderr,
+ "ERROR!!! can't seek to the beginning of %s\n", Path);
+ goto print_hint;
+ }
+ if (write(Fd, buf, sizeof(buf)) != sizeof(buf)) {
+ (void) fprintf(stderr,
+ "ERROR!!! can't write %d bytes to the beginning of %s\n",
+ (int)sizeof(buf), Path);
+ goto print_hint;
+ }
+/*
+ * Write a buffer load near the end of the file to bring it to the
+ * specified length. Leave the file open so lsof can find it.
+ */
+ if (SEEKF(Fd, (OFFSET_T)(sz - sizeof(buf)), SEEK_SET) < 0) {
+ (void) snprintf(szbuf, sizeof(szbuf) - 1, "%lld",
+ (unsigned long long)(sz - sizeof(buf)));
+ (void) fprintf(stderr, "ERROR!!! can't seek to %s in %s\n", szbuf,
+ Path);
+ goto print_hint;
+ }
+ if (write(Fd, buf, sizeof(buf)) != sizeof(buf)) {
+ (void) fprintf(stderr,
+ "ERROR!!! can't write %d bytes near the end of %s\n",
+ (int)sizeof(buf), Path);
+ goto print_hint;
+ }
+/*
+ * Fsync() the file.
+ */
+ if (fsync(Fd)) {
+ (void) fprintf(stderr, "ERROR!!! can't fsync %s\n", Path);
+ goto print_hint;
+ }
+
+/*
+ * If this dialect can't report offsets, disable the offset tests.
+ */
+ if (!do_offt) {
+ tofft = toffx = 1;
+ PrtMsg("WARNING!!! lsof can't return file offsets for this dialect,",
+ Pn);
+ PrtMsg(" so offset tests have been disabled.", Pn);
+ }
+/*
+ * Do file size test.
+ */
+ tsz = tstwlsof(TST_SZ, "-s", sz);
+/*
+ * If enabled, do offset tests.
+ */
+ if (!tofft)
+ tofft = tstwlsof(TST_OFFT, "-oo20", sz);
+ if (!toffx)
+ toffx = tstwlsof(TST_OFFX, "-oo2", sz);
+/*
+ * Compute exit value and exit.
+ */
+ if ((tsz != 1) || (tofft != 1) || (toffx != 1)) {
+ tcp = (char *)NULL;
+ xv = 1;
+ } else {
+ tcp = "OK";
+ xv = 0;
+ }
+ (void) PrtMsgX(tcp, Pn, cleanup, xv);
+ return(0);
+}
+
+
+#if defined(LT_DIAL_linux)
+/*
+ * ck_Linux_offset_support() -- see if lsof can report offsets for this
+ * Linux implementation
+ */
+
+static int
+ck_Linux_offset_support()
+{
+ char buf[1024]; /* lsof output line buffer */
+ int bufl = sizeof(buf); /* size of buf[] */
+ char *opv[5]; /* option vector for lsof */
+ int rv = 1; /* return value:
+ * 0 == no lsof offset support
+ * 1 == lsof offset support */
+/*
+ * Ask lsof to report the test's FD zero offset.
+ */
+ if (IsLsofExec())
+ return(0);
+ opv[0] = "-o";
+ snprintf(buf, bufl - 1, "-p%d", (int)getpid());
+ opv[1] = buf;
+ opv[2] = "-ad0";
+ opv[3] = "+w";
+ opv[4] = (char *)NULL;
+ if (ExecLsof(opv))
+ return(0);
+/*
+ * Read the lsof output. Look for a line with "WARNING: can't report offset"
+ * in it. If it is found, then this Linux lsof can't report offsets.
+ */
+ while(fgets(buf, bufl - 1, LsofFs)) {
+ if (strstr(buf, "WARNING: can't report offset")) {
+ rv = 0;
+ break;
+ }
+ }
+ (void) StopLsof();
+ return(rv);
+}
+#endif /* defined(LT_DIAL_linux) */
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+ if (Fd >= 0) {
+/*
+ * Close the test file.
+ *
+ * But first unlink it to discourage some kernel file system implementations
+ * (e.g., HFS on Apple Darwin, aka Mac OS X) from trying to fill the file's
+ * large holes. (Filling can take a long time.)
+ */
+ if (Path) {
+ (void) unlink(Path);
+ Path = (char *)NULL;
+ }
+ (void) close(Fd);
+ Fd = -1;
+ }
+}
+
+
+/*
+ * tstwlsof() -- test the open file with lsof
+ */
+
+static int
+tstwlsof(tt, opt, sz)
+ int tt; /* test type -- i.e., TST_* */
+ char *opt; /* additional lsof options */
+ OFFSET_T sz; /* expected size (and offset) */
+{
+ char buf[2048], buf1[2048]; /* temporary buffers */
+ LTfldo_t *cmdp; /* command pointer */
+ LTfldo_t *devp; /* device pointer */
+ char *em; /* error message pointer */
+ int ff = 0; /* file found status */
+ LTfldo_t *fop; /* field output pointer */
+ LTfldo_t *inop; /* inode number pointer */
+ LTdev_t lsofdc; /* lsof device components */
+ int nf; /* number of fields */
+ LTfldo_t *nmp; /* file name pointer */
+ LTfldo_t *offp; /* file offset pointer */
+ char *opv[4]; /* option vector for ExecLsof() */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ STATS sb; /* stat(2) buffer */
+ LTdev_t stdc; /* stat(2) device components */
+ LTfldo_t *szp; /* file size pointer */
+ LTfldo_t *tfop; /* temporary field output pointer */
+ int ti; /* temporary index */
+ LTfldo_t *typ; /* file type pointer */
+ int xv = 0; /* exit value */
+/*
+ * Check the test type.
+ */
+ switch (tt) {
+ case TST_OFFT:
+ case TST_OFFX:
+ case TST_SZ:
+ break;
+ default:
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! unknown test type: %d", tt);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Get test file's information.
+ */
+ if (STATF(Path, &sb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't stat(2) %s: %s", Path, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Extract components from test file's device number.
+ */
+ if ((em = ConvStatDev(&sb.st_dev, &stdc))) {
+ (void) PrtMsg(em, Pn);
+ return(0);
+ }
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ ti = 0;
+ if (opt && *opt)
+ opv[ti++] = opt;
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#else /* !defined(USE_LSOF_C_OPT) */
+ opv[ti++] = "--";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ opv[ti++] = Path;
+ opv[ti] = (char *)NULL;
+ if ((em = ExecLsof(opv))) {
+ (void) PrtMsg(em, Pn);
+ return(0);
+ }
+/*
+ * Read lsof output.
+ */
+ while (!ff && (fop = RdFrLsof(&nf, &em))) {
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != MyPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line.
+ *
+ * Scan for device number, inode number, name, offset, size, and type
+ * fields.
+ */
+ if (!pids)
+ break;
+ devp = inop = nmp = offp = szp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch(fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_NAME:
+ nmp = fop;
+ break;
+ case LSOF_FID_OFFSET:
+ offp = fop;
+ break;
+ case LSOF_FID_SIZE:
+ szp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the results of the file descriptor field scan.
+ *
+ * (Don't compare path names because of symbolic link interference.)
+ */
+ if (!devp || !inop || !nmp || !typ)
+ break;
+ if (strcasecmp(typ->v, "reg") && strcasecmp(typ->v, "vreg"))
+ break;
+ if (ConvLsofDev(devp->v, &lsofdc))
+ break;
+ if ((stdc.maj != lsofdc.maj)
+ || (stdc.min != lsofdc.min)
+ || (stdc.unit != lsofdc.unit))
+ break;
+ (void) snprintf(buf, sizeof(buf) - 1, "%llu",
+ (unsigned long long)sb.st_ino);
+ buf[sizeof(buf) - 1] = '\0';
+ if (strcmp(inop->v, buf))
+ break;
+ /*
+ * The specifed file has been located. Check its size or offset,
+ * according to the tt argument.
+ */
+ ff = 1;
+ switch (tt) {
+ case TST_OFFT:
+ case TST_SZ:
+
+ /*
+ * Test the size as an offset in decimal with a leading "0t", or
+ * test the size as a size in decimal.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1,
+ (tt == TST_SZ) ? "%llu" : "0t%llu",
+ (unsigned long long)sz);
+ buf[sizeof(buf) - 1] = '\0';
+ tfop = (tt == TST_SZ) ? szp : offp;
+ if (!tfop || strcmp(tfop->v, buf)) {
+ (void) snprintf(buf1, sizeof(buf1) - 1,
+ "%s mismatch: expected %s, got %s",
+ (tt == TST_SZ) ? "size" : "0t offset",
+ buf,
+ tfop ? tfop->v : "nothing");
+ buf1[sizeof(buf1) - 1] = '\0';
+ (void) PrtMsg(buf1, Pn);
+ xv = 0;
+ } else
+ xv = 1;
+ break;
+ case TST_OFFX:
+
+ /*
+ * Test the size as an offset in hex.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1, "0x%llx",
+ (unsigned long long)sz);
+ buf[sizeof(buf) - 1] = '\0';
+ if (!offp || strcmp(offp->v, buf)) {
+ (void) snprintf(buf1, sizeof(buf1) - 1,
+ "0x offset mismatch: expected %s, got %s",
+ buf,
+ offp ? offp->v : "nothing");
+ buf1[sizeof(buf1) - 1] = '\0';
+ (void) PrtMsg(buf1, Pn);
+ xv = 0;
+ } else
+ xv = 1;
+ }
+ break;
+ }
+ }
+ (void) StopLsof();
+ if (em) {
+
+ /*
+ * RdFrLsof() encountered an error.
+ */
+ (void) PrtMsg(em, Pn);
+ xv = 0;
+ }
+ if (!ff) {
+ (void) snprintf(buf, sizeof(buf) - 1, "%s not found by lsof", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ PrtMsg(buf, Pn);
+ xv = 0;
+ }
+ return(xv);
+}
+#endif /* defined(LT_BIG) */
diff --git a/tests/LTdnlc.c b/tests/LTdnlc.c
new file mode 100644
index 0000000..66c6262
--- /dev/null
+++ b/tests/LTdnlc.c
@@ -0,0 +1,426 @@
+/*
+ * LTdnlc.c -- Lsof Test Dynamic Name Lookup Cache test
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+
+/*
+ * Pre-definitions that may be revoked by specific dialects
+ */
+
+#define DO_TEST /* do the test */
+
+
+/*
+ * Dialect-specific items
+ */
+
+
+#if defined(LT_DIAL_aix)
+/*
+ * AIX-specific items
+ */
+
+#undef DO_TEST
+#endif /* defined(LT_DIAL_aix) */
+
+
+#if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific items
+ */
+
+# if LT_VERS<800
+#undef DO_TEST
+# endif /* LT_VERS<800 */
+#endif /* defined(LT_DIAL_darwin) */
+
+
+/*
+ * Local definitions
+ */
+
+#define ATTEMPT_CT 5 /* number of lsof CWD lookup attempts */
+#define LSPATH "/bin/ls" /* path to ls(1) */
+#define SUCCESS_THRESH 50.0 /* success threshold */
+
+
+/*
+ * Globals
+ */
+
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *FindLsofCwd,(int *ff, LTdev_t *cwddc, char *ibuf));
+
+
+/*
+ * Main program
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ char cwd[MAXPATHLEN + 1]; /* CWD */
+ LTdev_t cwddc; /* CWD device components */
+ char *em; /* error message pointer */
+ int ff; /* FindFile() file-found flag */
+ int fpathct; /* full path found count */
+ char ibuf[32]; /* inode buffer */
+ char lsbuf[2048 + MAXPATHLEN + 1]; /* ls(1) system() command */
+ double pct; /* performance percentage */
+ struct stat sb; /* CWD stat(2) results */
+ int ti; /* temporary index */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "h", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h] [-p path]", Pn);
+ PrtMsgX(" -h print help (this panel)", Pn, cleanup, xv);
+ }
+
+#if !defined(DO_TEST)
+/*
+ * If the dialect has disabled the test, echo that result and exit with
+ * a successful return code.
+ */
+ (void) PrtMsgX(LT_DONT_DO_TEST, Pn, cleanup, 0);
+#endif /* !defined(DO_TEST) */
+
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Get the CWD and form the ls(1) system() command.
+ */
+
+#if defined(USE_GETCWD)
+ em = "getcwd";
+ if (!getcwd(cwd, sizeof(cwd)))
+#else /* ! defined(USE_GETCWD) */
+ em = "getwd";
+ if (!getwd(cwd))
+#endif /* defined(USE_GETCWD) */
+
+ {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! %s() error: %s", em, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+ (void) snprintf(lsbuf, sizeof(lsbuf) - 1, "%s %s > /dev/null 2>&1",
+ LSPATH, cwd);
+/*
+ * Get the CWD stat(2) results.
+ */
+ if (stat(cwd, &sb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! stat(%s) error: %s", cwd, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+ if ((em = ConvStatDev(&sb.st_dev, &cwddc)))
+ PrtMsgX(em, Pn, cleanup, 1);
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u", (unsigned int)sb.st_ino);
+ ibuf[sizeof(ibuf) - 1] = '\0';
+/*
+ * Loop ATTEMPT_CT times.
+ */
+ for (fpathct = ti = 0; ti < ATTEMPT_CT; ti++) {
+
+ /*
+ * Call ls(1) to list the CWD to /dev/null.
+ */
+ (void) system(lsbuf);
+ /*
+ * Call lsof to look up its own CWD -- i.e., this one.
+ */
+ if ((em = FindLsofCwd(&ff, &cwddc, ibuf))) {
+
+ /*
+ * FindLsofCwd() returned a message. Decode it via ff.
+ */
+ if (ff == -1)
+ PrtMsgX(em, Pn, cleanup, 1);
+ else if (ff == 1) {
+
+ /*
+ * This shouldn't happen. If FindLsof() found lsof's CWD, it
+ * should set ff to one and return NULL.
+ */
+ PrtMsgX("ERROR!!! inconsistent FindLsofCwd() return", Pn,
+ cleanup, 1);
+ }
+ } else if (ff == 1) {
+ fpathct++;
+ }
+ }
+/*
+ * Compute, display, and measure the success percentage.
+ */
+ pct = ((double)fpathct * (double)100.0) / (double)ATTEMPT_CT;
+ PrtMsg((char *)NULL, Pn);
+ (void) printf("%s found: %.2f%%\n", cwd, pct); /* NeXT snpf.c has no
+ * %f support */
+ MsgStat = 1;
+ if (pct < (double)SUCCESS_THRESH) {
+ PrtMsg("ERROR!!! the find rate was too low.", Pn);
+ if (!fpathct) {
+ (void) PrtMsg(
+ "Hint: since the find rate is zero, it may be that this file",
+ Pn);
+ (void) PrtMsg(
+ "system does not fully participate in kernel DNLC processing",
+ Pn);
+ (void) PrtMsg(
+ "-- e.g., NFS file systems often do not, /tmp file systems",
+ Pn);
+ (void) PrtMsg(
+ "sometimes do not, Solaris loopback file systems do not.\n",
+ Pn);
+ (void) PrtMsg(
+ "As a work-around rebuild and test lsof on a file system that",
+ Pn);
+ (void) PrtMsg(
+ "fully participates in kernel DNLC processing.\n",
+ Pn);
+ (void) PrtMsg("See 00FAQ and 00TEST for more information.", Pn);
+ }
+ exit(1);
+ }
+/*
+ * Exit successfully.
+ */
+ (void) PrtMsgX("OK", Pn, cleanup, 0);
+ return(0);
+}
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+}
+
+
+/*
+ * FindLsofCwd() -- find the lsof CWD
+ */
+
+static char *
+FindLsofCwd(ff, cwddc, ibuf)
+ int *ff; /* file-found response receptor */
+ LTdev_t *cwddc; /* CWD device components */
+ char *ibuf; /* CWD inode number in ASCII */
+{
+ char *cp; /* temporary character pointer */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTdev_t devdc; /* devp->v device components */
+ LTfldo_t *devp; /* device pointer */
+ LTfldo_t *fop; /* field output pointer */
+ LTfldo_t *inop; /* inode number pointer */
+ int nf; /* number of fields */
+ LTfldo_t *nmp; /* name pointer */
+ char *opv[3]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ int ti; /* temporary integer */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Check the argument pointers.
+ *
+ * Set the file-found response false.
+ */
+ if (!ff || !cwddc || !ibuf)
+ (void) PrtMsgX("ERROR!!! missing argument to FindFile()",
+ Pn, cleanup, 1);
+ *ff = 0;
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ opv[0] = "-clsof";
+ opv[1] = "-adcwd";
+ opv[2] = (char *)NULL;
+ if ((cem = ExecLsof(opv))) {
+ *ff = -1;
+ return(cem);
+ }
+/*
+ * Read lsof output.
+ */
+ while (!*ff && (fop = RdFrLsof(&nf, &cem))) {
+ if (cem) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ *ff = -1;
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != LsofPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Make sure it's for the expected
+ * PID and its type is "cwd".
+ */
+ if (!pids)
+ break;
+ if (strcasecmp(fop->v, "cwd"))
+ break;
+ /*
+ * Scan for device, inode, name, and type fields.
+ */
+ devp = inop = nmp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_NAME:
+ nmp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the device, inode, and type of the file.
+ */
+ if (!devp || !inop || !nmp || !typ)
+ break;
+ if (strcasecmp(typ->v, "dir") && strcasecmp(typ->v, "vdir"))
+ break;
+ if ((cem = ConvLsofDev(devp->v, &devdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if ((cwddc->maj != devdc.maj)
+ || (cwddc->min != devdc.min)
+ || (cwddc->unit != devdc.unit)
+ || strcmp(inop->v, ibuf)
+ ) {
+ break;
+ }
+ /*
+ * Check the name for spaces. If it has none, set a file-found
+ * response.
+ */
+ if (!(cp = strchr(nmp->v, ' ')))
+ *ff = 1;
+ else {
+
+ /*
+ * If a parenthesized file system name follows the space in the
+ * file's name, it probably is an NFS file system name and can
+ * be ignored. Accordingly set a file-found response.
+ */
+ if ((*(cp + 1) == '(') && *(cp + 2) && !strchr(cp + 2, ' ')) {
+ if ((cp = strchr(cp + 2, ')')) && !*(cp + 1))
+ *ff = 1;
+ }
+ }
+ }
+ }
+/*
+ * Clean up and return.
+ */
+ (void) StopLsof();
+ if (pem) {
+ *ff = -1;
+ return(pem);
+ }
+ return((char *)NULL);
+}
diff --git a/tests/LTlib.c b/tests/LTlib.c
new file mode 100644
index 0000000..be15af1
--- /dev/null
+++ b/tests/LTlib.c
@@ -0,0 +1,1104 @@
+/*
+ * LTlib.c -- the lsof test library
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+
+
+/*
+ * Pre-defintions that may be changed by a specific dialect
+ */
+
+#define X2DEV_T unsigned int /* cast for result of x2dev() */
+#define XDINDEV 8 /* number of hex digits in an lsof
+ * device field -- should be
+ * 2 X sizeof(X2DEV_T) */
+
+
+#if defined(LT_DIAL_aix)
+/*
+ * AIX-specific items
+ */
+
+#include <sys/sysmacros.h>
+
+# if defined(LT_AIXA) && LT_AIXA>=1
+
+/*
+ * Note: the DEVNO64 and ISDEVNO54 #define's come from <sys/sysmacros.h>, but
+ * only when _KERNEL is #define'd.
+ */
+
+#undef DEVNO64
+#define DEVNO64 0x8000000000000000LL
+#undef ISDEVNO64
+#define ISDEVNO64(d) (((ulong)(d) & DEVNO64) ? 1 : 0)
+
+/*
+ * Define major and minor extraction macros that work on 64 bit AIX
+ * architectures.
+ */
+
+#define major_S(d) (ISDEVNO64(d) ? major64(d) : minor(d & ~SDEV_REMOTE))
+#define minor_S(d) (ISDEVNO64(d) ? (minor64(d) & ~SDEV_REMOTE) : minor(d))
+#undef X2DEV_T
+#define X2DEV_T unsigned long long
+#undef XDINDEV
+#define XDINDEV 16
+#define major_X(dp, em) major_S(x2dev(dp, em))
+#define minor_X(dp, em) minor_S(x2dev(dp, em))
+# endif /* defined(LT_AIXA) && LT_AIXA>=1 */
+
+#endif /* defined(LT_DIAL_aix) */
+
+
+#if defined(LT_DIAL_bsdi)
+/*
+ * BSDI-specific items
+ */
+
+#define minor_S(dev) dv_subunit(dev)
+#define unit_S(dev) dv_unit(dev)
+#define minor_X(dp, em) dv_subunit(x2dev(dp, em))
+#define unit_X(dp, em) dv_unit(x2dev(dp, em))
+#endif /* defined(LT_DIAL_bsdi) */
+
+
+#if defined(LT_DIAL_osr)
+/*
+ * OpenUNIX-specific items
+ */
+
+#include <sys/sysmacros.h>
+#endif /* defined(LT_DIAL_osr) */
+
+
+#if defined(LT_DIAL_ou)
+/*
+ * OpenUNIX-specific items
+ */
+
+#include <sys/mkdev.h>
+#endif /* defined(LT_DIAL_ou) */
+
+
+#if defined(LT_DIAL_solaris)
+/*
+ * Solaris-specific items
+ */
+
+#include <sys/sysmacros.h>
+
+
+/*
+ * Define maximum major device number in a stat(2) dev_t
+ */
+
+# if LT_VERS>=20501
+#define LT_MJX L_MAXMAJ /* Get maximum major device number from
+ * <sys/sysmacros.h>. */
+# else /* LT_VERS<20501 */
+#define LT_MJX 0x3fff /* Avoid <sys/sysmacros.h> when
+ * Solaris < 2.5.1. */
+# endif /* LT_VERS>=20501 */
+
+#define major_S(dev) ((int)((dev >> L_BITSMINOR) & LT_MJX))
+#define minor_S(dev) ((int)(dev & L_MAXMIN))
+
+# if defined(LT_K64)
+
+/*
+ * Solaris 64 bit kernel
+ */
+
+#undef X2DEV_T
+#define X2DEV_T unsigned long long
+#undef XDINDEV
+#define XDINDEV 16
+
+#define major_X(dp, em) ((int)((x2dev(dp, em) >> 32) & 0xffffffff))
+#define minor_X(dp, em) ((int)(x2dev(dp, em) & 0xffffffff))
+# else /* !defined(LT_K64) */
+
+/*
+ * Solaris 32 bit kernel
+ */
+
+#define major_X(dp, em) ((int)((x2dev(dp, em) >> L_BITSMINOR) & LT_MJX))
+#define minor_X(dp, em) ((int)(x2dev(dp, em) & L_MAXMIN))
+# endif /* LT_K64 */
+#endif /* defined(LT_DIAL_solaris) */
+
+
+#if defined(LT_DIAL_uw)
+/*
+ * UnixWare-specific items
+ */
+
+#include <sys/mkdev.h>
+#endif /* defined(LT_DIAL_uw) */
+
+
+/*
+ * Global variables
+ */
+
+int LsofFd = -1; /* lsof pipe FD */
+FILE *LsofFs = (FILE *)NULL; /* stream for lsof pipe FD */
+char *LsofPath = (char *)NULL; /* path to lsof executable */
+pid_t LsofPid = (pid_t)0; /* PID of lsof child process */
+int LTopt_h = 0; /* "-h" option's switch value */
+char *LTopt_p = (char *)NULL; /* "-p path" option's path value */
+int MsgStat = 0; /* message status: 1 means prefix needs
+ * to be issued */
+
+
+/*
+ * Local static variables
+ */
+
+static int Afo = 0; /* Fo[] structures allocated */
+static char *GOv = (char *)NULL; /* option `:' value pointer */
+static int GOx1 = 1; /* first opt[][] index */
+static int GOx2 = 0; /* second opt[][] index */
+static LTfldo_t *Fo = (LTfldo_t *)NULL; /* allocated LTfldo_t structures */
+static int Ufo = 0; /* Fo[] structures used */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void closepipe,(void));
+_PROTOTYPE(static void getlsofpath,(void));
+_PROTOTYPE(static int GetOpt,(int ct, char *opt[], char *rules, char **em,
+ char *pn));
+_PROTOTYPE(static X2DEV_T x2dev,(char *x, char **em));
+
+
+/*
+ * Default major, minor, and unit macros.
+ */
+
+#if !defined(major_S)
+#define major_S major
+#endif /* defined(major_S) */
+
+#if !defined(minor_S)
+#define minor_S minor
+#endif /* defined(minor_S) */
+
+#if !defined(unit_S)
+#define unit_S(x) 0
+#endif /* defined(unit_S) */
+
+#if !defined(major_X)
+#define major_X(dp, em) major(x2dev(dp, em))
+#endif /* defined(major_X) */
+
+#if !defined(minor_X)
+#define minor_X(dp, em) minor(x2dev(dp, em))
+#endif /* defined(minor_X) */
+
+#if !defined(unit_X)
+#define unit_X(dp, em) 0
+#endif /* defined(unit_X) */
+
+
+/*
+ * CanRdKmem() -- can lsof read kernel memory devices?
+ */
+
+char *
+CanRdKmem()
+{
+
+#if defined(LT_KMEM)
+ char buf[2048]; /* temporary buffer */
+ char *dn; /* memory device name */
+ char *em; /* error message pointer */
+ int fd; /* temporary file descriptor */
+ struct stat sb; /* memory device stat(2) buffer */
+ int ti; /* temporary integer */
+/*
+ * Get the lsof path. If it is not the default, check no further.
+ */
+ (void) getlsofpath();
+ if (!strcmp(LsofPath, LT_DEF_LSOF_PATH))
+ return((char *)NULL);
+/*
+ * Check /dev/kmem access.
+ */
+ dn = "/dev/kmem";
+ if (stat(dn, &sb)) {
+ em = "stat";
+
+kmem_error:
+
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't %s(%s): %s\n", em, dn, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+ if ((fd = open(dn, O_RDONLY, 0)) < 0) {
+ em = "open";
+ goto kmem_error;
+ }
+ (void) close(fd);
+/*
+ * Check /dev/mem access.
+ */
+ dn = "/dev/mem";
+ if (stat(dn, &sb)) {
+
+ /*
+ * If /dev/mem can't be found, ignore the error.
+ */
+ return((char *)NULL);
+ }
+ if ((fd = open(dn, O_RDONLY, 0)) < 0) {
+ em = "open";
+ goto kmem_error;
+ }
+ (void) close(fd);
+#endif /* defined(LT_KMEM) */
+
+ return((char *)NULL);
+}
+
+
+/*
+ * closepipe() -- close pipe from lsof
+ */
+
+static void
+closepipe()
+{
+ if (LsofFd >= 0) {
+
+ /*
+ * A pipe from lsof is open. Close it and the associated stream.
+ */
+ if (LsofFs) {
+ (void) fclose(LsofFs);
+ LsofFs = (FILE *)NULL;
+ }
+ (void) close(LsofFd);
+ LsofFd = -1;
+ }
+}
+
+
+/*
+ * ConvLsofDev() -- convert lsof device string
+ *
+ * Note: this function is dialect-specific.
+ */
+
+char *
+ConvLsofDev(dev, ldev)
+ char *dev; /* lsof device string -- the value to the
+ * LSOF_FID_DEVN field of a LSOF_FID_FD block
+ * (see lsof_fields.h) */
+ LTdev_t *ldev; /* results are returned to this structure */
+{
+ char *dp; /* device pointer */
+ char *em; /* error message pointer */
+ int tlen; /* temporary length */
+/*
+ * Check function arguments.
+ *
+ * Establish values for decoding the device string.
+ */
+ if (!dev)
+ return("ERROR!!! no ConvLsofDev() device");
+ if (!ldev)
+ return("ERROR!!! no ConvLsofDev() result pointer");
+ if (strncmp(dev, "0x", 2))
+ return("ERROR!!! no leading 0x in ConvLsofDev() device");
+ dp = dev + 2;
+ if (((tlen = (int)strlen(dp)) < 1) || (tlen > XDINDEV))
+ return("ERROR!!! bad ConvLsofDev() device length");
+/*
+ * Use the pre-defined *_X() macros to do the decomposition.
+ */
+ ldev->maj = (unsigned int)major_X(dp, &em);
+ if (em)
+ return(em);
+ ldev->min = (unsigned int)minor_X(dp, &em);
+ if (em)
+ return(em);
+ ldev->unit = (unsigned int)unit_X(dp, &em);
+ return(em);
+}
+
+
+/*
+ * ConvStatDev() -- convert stat(2) device number
+ *
+ * Note: this function is dialect-specific.
+ */
+
+char *
+ConvStatDev(dev, ldev)
+ dev_t *dev; /* device number to be converted */
+ LTdev_t *ldev; /* results are returned to this structure */
+{
+
+/*
+ * Check function arguments.
+ */
+ if (!dev)
+ return("ERROR!!! no ConvStatDev() device");
+ if (!ldev)
+ return("ERROR!!! no ConvStatDev() result pointer");
+/*
+ * Use the pre-defined *_S() macros to do the decomposition.
+ */
+ ldev->maj = (unsigned int)major_S(*dev);
+ ldev->min = (unsigned int)minor_S(*dev);
+ ldev->unit = (unsigned int)unit_S(*dev);
+ return((char *)NULL);
+}
+
+
+/*
+ * ExecLsof() -- execute lsof with full field output and a NUL field terminator
+ * in a child process
+ */
+
+char *
+ExecLsof(opt)
+ char **opt; /* lsof options -- a pointer to an
+ * array of character pointers,
+ * terminated by a NULL pointer */
+{
+ static char **av = (char **)NULL; /* lsof argument vector, dynamically
+ * allocated */
+ static int ava = 0; /* **av entries allocated */
+ char buf[2048]; /* temporary buffer */
+ char *em; /* error message pointer */
+ int fd; /* temporary file descriptor */
+ int optc; /* option count */
+ int nf; /* number of files */
+ int p[2]; /* pipe FDs */
+ char **tcpp; /* temporary character pointer
+ * pointer */
+ int ti; /* temporary integer */
+ int tlen; /* temporary length */
+ pid_t tpid; /* temporary PID holder */
+/*
+ * It's an error if lsof is already in execution or if no lsof options
+ * were supplied.
+ */
+ (void) getlsofpath();
+ if (LsofPid)
+ return("ERROR!!! ExecLsof() says lsof is already in execution");
+ if (!opt)
+ return("ERROR!!! no ExecLsof() option list");
+ for (optc = 0, tcpp = opt; *tcpp; optc++, tcpp++)
+ ;
+/*
+ * Make sure lsof is executable.
+ */
+ if ((em = IsLsofExec()))
+ return(em);
+/*
+ * Open a pipe through which lsof can return output.
+ */
+ if (pipe(p)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't open pipe: %s", strerror(errno));
+ return(MkStrCpy(buf, &ti));
+ }
+/*
+ * Allocate and build an argument vector. The first entry will be set
+ * to "lsof", the second to "-wFr", and the third to "-F0". Additional
+ * entries will be set as supplied by the caller.
+ */
+ if ((optc + 4) > ava) {
+ tlen = (int)(sizeof(char *) * (optc + 4));
+ if (!av)
+ av = (char **)malloc(tlen);
+ else
+ av = (char **)realloc((void *)av, tlen);
+ if (!av) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "LTlib: ExecLsof() can't allocat pointers for %d arguments",
+ optc + 4);
+ return(MkStrCpy(buf, &ti));
+ }
+ ava = optc + 4;
+ }
+ for (ti = 0, tcpp = opt; ti < (optc + 3); ti++) {
+ switch(ti) {
+ case 0:
+ av[ti] = "lsof";
+ break;
+ case 1:
+ av[ti] = "-wFr";
+ break;
+ case 2:
+ av[ti] = "-F0";
+ break;
+ default:
+ av[ti] = *tcpp;
+ tcpp++;
+ }
+ }
+ av[ti] = (char *)NULL;
+/*
+ * Fork a child process to run lsof.
+ */
+ switch((tpid = fork())) {
+ case (pid_t)0:
+
+ /*
+ * This is the child process.
+ *
+ * First close all file descriptors except the output side of the pipe.
+ *
+ * Make the output side of the pipe STDOUT and STDERR.
+ */
+ for (fd = 0, nf = getdtablesize(); fd < nf; fd++) {
+ if (fd == p[1])
+ continue;
+ (void) close(fd);
+ }
+ if (p[1] != 1)
+ (void) dup2(p[1], 1);
+ if (p[1] != 2)
+ (void) dup2(p[1], 2);
+ if ((p[1] != 1) && (p[1] != 2))
+ (void) close(p[1]);
+ /*
+ * Execute lsof.
+ */
+ (void) execv(LsofPath, av);
+ _exit(0); /* (Shouldn't get here.) */
+ case (pid_t)-1:
+
+ /*
+ * A fork error occurred. Form and return a message.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! ExecLsof() can't fork: %s", strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ default:
+
+ /*
+ * This is the parent.
+ *
+ * Save the lsof child PID.
+ *
+ * Close the output side of the pipe.
+ *
+ * Save the input side of the pipe as LsofFd; open a stream for it.
+ */
+ LsofPid = tpid;
+ (void) close(p[1]);
+ LsofFd = p[0];
+ if (!(LsofFs = fdopen(LsofFd, "r")))
+ return("ERROR!!! ExecLsof() can't open stream to lsof output FD");
+ }
+/*
+ * Wait a bit for lsof to start and put something in its pipe, then return
+ * an "All is well." response.
+ */
+ sleep(1);
+ return((char *)NULL);
+}
+
+
+/*
+ * getlsofpath() -- get lsof path, either from LT_LSOF_PATH in the environment
+ * or from LT_DEF_LSOF_PATH
+ */
+
+static void
+getlsofpath()
+{
+ char *tcp; /* temporary character pointer */
+ int ti; /* temporary integer */
+
+ if (LsofPath)
+ return;
+ if ((tcp = getenv("LT_LSOF_PATH")))
+ LsofPath = MkStrCpy(tcp, &ti);
+ else
+ LsofPath = LT_DEF_LSOF_PATH;
+}
+
+
+/*
+ * GetOpt() -- Local get option
+ *
+ * Borrowed from lsof's main.c source file.
+ *
+ * Liberally adapted from the public domain AT&T getopt() source,
+ * distributed at the 1985 UNIFORM conference in Dallas
+ *
+ * The modifications allow `?' to be an option character and allow
+ * the caller to decide that an option that may be followed by a
+ * value doesn't have one -- e.g., has a default instead.
+ */
+
+static int
+GetOpt(ct, opt, rules, em, pn)
+ int ct; /* option count */
+ char *opt[]; /* options */
+ char *rules; /* option rules */
+ char **em; /* error message return */
+ char *pn;
+{
+ register int c; /* character value */
+ register char *cp = (char *)NULL; /* character pointer */
+ char embf[2048]; /* error message buffer */
+ int tlen; /* temporary message length from
+ * MkStrCpy() */
+
+ *em = (char *)NULL;
+ if (GOx2 == 0) {
+
+ /*
+ * Move to a new entry of the option array.
+ *
+ * EOF if:
+ *
+ * Option list has been exhausted;
+ * Next option doesn't start with `-' or `+';
+ * Next option has nothing but `-' or `+';
+ * Next option is ``--'' or ``++''.
+ */
+ if (GOx1 >= ct
+ || (opt[GOx1][0] != '-' && opt[GOx1][0] != '+')
+ || !opt[GOx1][1])
+ return(EOF);
+ if (strcmp(opt[GOx1], "--") == 0 || strcmp(opt[GOx1], "++") == 0) {
+ GOx1++;
+ return(EOF);
+ }
+ GOx2 = 1;
+ }
+/*
+ * Flag `:' option character as an error.
+ *
+ * Check for a rule on this option character.
+ */
+ if ((c = opt[GOx1][GOx2]) == ':') {
+ (void) snprintf(embf, sizeof(embf) - 1,
+ "ERROR!!! colon is an illegal option character.");
+ embf[sizeof(embf) - 1] = '\0';
+ *em = MkStrCpy(embf, &tlen);
+ } else if (!(cp = strchr(rules, c))) {
+ (void) snprintf(embf, sizeof(embf) - 1,
+ "ERROR!!! illegal option character: %c", c);
+ embf[sizeof(embf) - 1] = '\0';
+ *em = MkStrCpy(embf, &tlen);
+ }
+ if (*em) {
+
+ /*
+ * An error was detected.
+ *
+ * Advance to the next option character.
+ *
+ * Return the character causing the error.
+ */
+ if (opt[GOx1][++GOx2] == '\0') {
+ GOx1++;
+ GOx2 = 0;
+ }
+ return(c);
+ }
+ if (*(cp + 1) == ':') {
+
+ /*
+ * The option may have a following value. The caller decides if it does.
+ *
+ * Don't indicate that an option of ``--'' is a possible value.
+ *
+ * Finally, on the assumption that the caller will decide that the possible
+ * value belongs to the option, position to the option following the
+ * possible value, so that the next call to GetOpt() will find it.
+ */
+ if(opt[GOx1][GOx2 + 1] != '\0') {
+ GOv = &opt[GOx1++][GOx2];
+ } else if (++GOx1 >= ct)
+ GOv = (char *)NULL;
+ else {
+ GOv = opt[GOx1];
+ if (strcmp(GOv, "--") == 0)
+ GOv = (char *)NULL;
+ else
+ GOx1++;
+ }
+ GOx2 = 0;
+ } else {
+
+ /*
+ * The option character stands alone with no following value.
+ *
+ * Advance to the next option character.
+ */
+ if (opt[GOx1][++GOx2] == '\0') {
+ GOx2 = 0;
+ GOx1++;
+ }
+ GOv = (char *)NULL;
+ }
+/*
+ * Return the option character.
+ */
+ return(c);
+}
+
+
+/*
+ * IsLsofExec() -- see if lsof is executable
+ */
+
+char *
+IsLsofExec()
+{
+ char buf[2048]; /* temporary buffer */
+ int len; /* temporary length */
+
+ (void) getlsofpath();
+ if (access(LsofPath, X_OK) < 0) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't execute %s: %s", LsofPath, strerror(errno));
+ return(MkStrCpy(buf, &len));
+ }
+ return((char *)NULL);
+}
+
+
+/*
+ * LTlibClean() -- clean up LTlib resource accesses
+ */
+
+void
+LTlibClean()
+{
+ (void) StopLsof();
+}
+
+
+/*
+ * MkStrCpy() -- make string copy
+ */
+
+char *
+MkStrCpy(src, len)
+ char *src; /* string source to copy */
+ int *len; /* returned length allocation */
+{
+ char *rp; /* return pointer */
+ int srclen; /* source string length */
+
+ if (!src) {
+ (void) fprintf(stderr, "ERROR!!! no string supplied to MkStrCpy()\n");
+ exit(1);
+ }
+ srclen = (int)strlen(src);
+ *len = srclen++;
+ if (!(rp = (char *)malloc(srclen))) {
+ (void) fprintf(stderr, "ERROR!!! MkStrCpy() -- no malloc() space");
+ exit(1);
+ }
+ (void) strcpy(rp, src);
+ return(rp);
+}
+
+
+/*
+ * PrtMsg() -- print message
+ */
+
+void
+PrtMsg(mp, pn)
+ char *mp; /* message pointer -- may be NULL to
+ * trigger space prefix initialization
+ */
+ char *pn; /* program name */
+{
+ static int pfxlen = -1; /* prefix length, based on program */
+ /* name -- computed on first call
+ * when pfxlen == -1 */
+ static char *pfx = (char *)NULL; /* prefix (spaces) */
+ int ti; /* temporary index */
+
+ if (pfxlen == -1) {
+
+ /*
+ * This is the first call. Compute the prefix length and build the
+ * prefix.
+ */
+ if (!pn)
+ pfxlen = 0;
+ else
+ pfxlen = (int)(strlen(pn));
+ pfxlen += (int)strlen(" ... ");
+ if (!(pfx = (char *)malloc(pfxlen + 1))) {
+ (void) printf( "ERROR!!! not enough space for %d space prefix\n",
+ pfxlen);
+ exit(1);
+ }
+ for (ti = 0; ti < pfxlen; ti++) {
+ pfx[ti] = ' ';
+ }
+ pfx[pfxlen] = '\0';
+ MsgStat = 0;
+ }
+/*
+ * Process the message.
+ */
+ if (MsgStat)
+ (void) printf("%s", pfx);
+ if (mp && *mp) {
+ (void) printf("%s\n", mp);
+ MsgStat = 1;
+ }
+}
+
+
+/*
+ * PrtMsgX() -- print message and exit
+ */
+
+void
+PrtMsgX(mp, pn, f, xv)
+ char *mp; /* message pointer */
+ char *pn; /* program name */
+ void (*f)(); /* clean-up function pointer */
+ int xv; /* exit value */
+{
+ if (mp)
+ PrtMsg(mp, pn);
+ if (f)
+ (void) (*f)();
+ (void) LTlibClean();
+ exit(xv);
+}
+
+
+/*
+ * RdFrLsof() -- read from lsof
+ */
+
+LTfldo_t *
+RdFrLsof(nf, em)
+ int *nf; /* number of fields receiver */
+ char **em; /* error message pointer receiver */
+{
+ char buf[2048]; /* temporary buffer */
+ int bufl = (int)sizeof(buf); /* size of buf[] */
+ char *blim = &buf[bufl - 1]; /* buf[] limit (last character
+ * address) */
+ char *fsp; /* field start pointer */
+ char *tcp; /* temporary character pointer */
+ LTfldo_t *tfop; /* temporary field output pointer */
+ int ti; /* temporary index */
+ int tlen; /* remporary length */
+ char *vp; /* value character pointer */
+/*
+ * Check for errors.
+ */
+ if (!em)
+ return((LTfldo_t *)NULL);
+ if (!nf) {
+ *em = "ERROR!!! RdFrLsof() not given a count return pointer";
+ return((LTfldo_t *)NULL);
+ }
+ *em = (char *)NULL;
+ *nf = 0;
+/*
+ * If fields are in use, release their resources.
+ */
+ for (ti = 0, tfop = Fo; (ti < Ufo); ti++, tfop++) {
+ if (tfop->v)
+ (void) free((void *)tfop->v);
+ }
+ Ufo = 0;
+/*
+ * Read a line from lsof.
+ */
+ if (!fgets(buf, bufl - 2, LsofFs)) {
+
+ /*
+ * An lsof pipe EOF has been reached. Indicate that with a NULL
+ * pointer return, coupled with a NULL error message return pointer
+ * (set above), and a field count of zero (set above).
+ */
+ return((LTfldo_t *)NULL);
+ }
+/*
+ * Parse the lsof line, allocating field output structures as appropriate.
+ *
+ * It is expected that fields will end in a NUL ('\0') or a NL ('\0') and
+ * that a NL ends all fields in the lsof line.
+ */
+ for (tcp = buf, Ufo = 0; (*tcp != '\n') && (tcp < blim); tcp++) {
+
+ /*
+ * Start a new field. The first character is the LSOF_FID_*.
+ *
+ * First allocate an LTfldo_t structure.
+ */
+ if (Ufo >= Afo) {
+
+ /*
+ * More LTfldo_t space is required.
+ */
+ Afo += LT_FLDO_ALLOC;
+ tlen = (int)(Afo * sizeof(LTfldo_t));
+ if (Fo)
+ Fo = (LTfldo_t *)realloc(Fo, tlen);
+ else
+ Fo = (LTfldo_t *)malloc(tlen);
+ if (!Fo) {
+
+ /*
+ * A serious error has occurred; no LTfldo_t space is available.
+ */
+ (void) snprintf(buf, bufl,
+ "ERROR!!! RdFrLsof() can't allocate %d pointer bytes",
+ tlen);
+ *em = MkStrCpy(buf, &ti);
+ *nf = -1;
+ return((LTfldo_t *)NULL);
+ }
+ }
+ tfop = Fo + Ufo;
+ tfop->v = (char *)NULL;
+ Ufo++;
+ /*
+ * Save the LSOF_FID_* character. Then compute the field value length,
+ * and make a copy of it.
+ */
+ tfop->ft = *tcp++;
+ fsp = tcp;
+ tlen = 0;
+ while (*tcp && (*tcp != '\n') && (tcp < blim)) {
+ tcp++;
+ tlen++;
+ }
+ if (!(vp = (char *)malloc(tlen + 1))) {
+
+ /*
+ * A serious error has occurred; there's no space for the field value.
+ */
+ (void) snprintf(buf, bufl,
+ "ERROR!!! RdFrLsof() can't allocate %d field bytes", tlen + 1);
+ *em = MkStrCpy(buf, &ti);
+ *nf = -1;
+ return((LTfldo_t *)NULL);
+ }
+ (void) memcpy((void *)vp, (void *)fsp, tlen);
+ vp[tlen] = '\0';
+ tfop->v = vp;
+ if (*tcp == '\n')
+ break;
+ if (tcp >= blim) {
+
+ /*
+ * The lsof line has no NL terminator; that's an error.
+ */
+ *em = "ERROR!!! RdFrLsof() didn't find a NL";
+ *nf = -1;
+ return((LTfldo_t *)NULL);
+ }
+ }
+/*
+ * The end of the lsof line has been reached. If no fields were assembled,
+ * return an error indicate. Otherwise return the fields and their count.
+ */
+ if (!Ufo) {
+ *em = "ERROR!!! RdFrLsof() read an empty lsof line";
+ *nf = -1;
+ return((LTfldo_t *)NULL);
+ }
+ *nf = Ufo;
+ *em = (char *)NULL;
+ return(Fo);
+}
+
+
+/*
+ * ScanArg() -- scan arguments
+ */
+
+int
+ScanArg(ac, av, opt, pn)
+ int ac; /* argument count */
+ char *av[]; /* argument pointers */
+ char *opt; /* option string */
+ char *pn; /* program name */
+{
+ char *em; /* pointer to error message returned by
+ * GetOpt() */
+ char embf[2048]; /* error message buffer */
+ int rv = 0; /* return value */
+ int tc; /* temporary character value */
+/*
+ * Preset possible argument values.
+ */
+ LTopt_h = 0;
+ if (LTopt_p) {
+ (void) free((void *)LTopt_p);
+ LTopt_p = (char *)NULL;
+ }
+/*
+ * Process the options according to the supplied option string.
+ */
+ while ((tc = GetOpt(ac, av, opt, &em, pn)) != EOF) {
+ if (em) {
+ rv = 1;
+ PrtMsg(em, pn);
+ continue;
+ }
+ switch (tc) {
+ case 'h':
+ LTopt_h = 1;
+ break;
+ case 'p':
+ if (!GOv || *GOv == '-' || *GOv == '+') {
+ rv = 1;
+ (void) PrtMsg("ERROR!!! -p not followed by a path", pn);
+ } else
+ LTopt_p = GOv;
+ break;
+ default:
+ rv = 1;
+ (void) snprintf(embf, sizeof(embf) - 1,
+ "ERROR!!! unknown option: %c", tc);
+ PrtMsg(embf, pn);
+ }
+ }
+ for (; GOx1 < ac; GOx1++) {
+
+ /*
+ * Report extraneous arguments.
+ */
+ rv = 1;
+ (void) snprintf(embf, sizeof(embf) - 1,
+ "ERROR!!! extraneous option: \"%s\"", av[GOx1]);
+ PrtMsg(embf, pn);
+ }
+ return(rv);
+}
+
+
+/*
+ * StopLsof() -- stop a running lsof process and close the pipe from it
+ */
+
+void
+StopLsof()
+{
+ pid_t pid;
+
+ if (LsofPid) {
+
+ /*
+ * An lsof child process may be active. Wait for (or kill) it.
+ */
+ pid = wait3(NULL, WNOHANG, NULL);
+ if (pid != LsofPid) {
+ (void) kill(LsofPid, SIGKILL);
+ sleep(2);
+ pid = wait3(NULL, WNOHANG, NULL);
+ }
+ LsofPid = (pid_t)0;
+ }
+ (void) closepipe();
+}
+
+
+/*
+ * x2dev() -- convert hex string to device number
+ */
+
+static X2DEV_T
+x2dev(x, em)
+ char *x; /* hex string */
+ char **em; /* error message receiver */
+{
+ char buf[2048]; /* temporary message buffer */
+ int c; /* character holder */
+ X2DEV_T dev; /* device number result */
+ char *wx; /* working hex string pointer */
+ int xl; /* hex string length */
+
+ if (!x || !*x) {
+ *em = "ERROR!!! no hex string supplied to x2dev()";
+ return(0);
+ }
+ wx = strncasecmp(x, "0x", 2) ? x : (x + 2);
+ if (((xl = (int)strlen(wx)) < 1) || (xl > XDINDEV)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! x2dev(\"%s\") bad length: %d", x, xl + 2);
+ buf[sizeof(buf) - 1] = '\0';
+ *em = MkStrCpy(buf, &c);
+ return(0);
+ }
+/*
+ * Assemble the device number result from the hex string.
+ */
+ for (dev = (X2DEV_T)0; *wx; wx++) {
+ if (isdigit((unsigned char)*wx)) {
+ dev = (dev << 4) | (unsigned int)(((int)*wx - (int)'0') & 0xf);
+ continue;
+ }
+ c = (int) tolower((unsigned char)*wx);
+ if ((c >= (int)'a') && (c <= (int)'f')) {
+ dev = (dev << 4) | (unsigned int)((c - 'a' + 10) & 0xf);
+ continue;
+ }
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! x2dev(\"%s\") non-hex character: %c", x, c);
+ *em = MkStrCpy(buf, &c);
+ }
+/*
+ * Return result and no error indication.
+ */
+ *em = (char *)NULL;
+ return(dev);
+}
diff --git a/tests/LTlock.c b/tests/LTlock.c
new file mode 100644
index 0000000..04bf649
--- /dev/null
+++ b/tests/LTlock.c
@@ -0,0 +1,769 @@
+/*
+ * LTlock.c -- Lsof Test locking tests
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+
+#if defined(LT_DIAL_aix)
+/*
+ * AIX-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_aix) */
+
+
+#if defined(LT_DIAL_bsdi)
+/*
+ * BSDI-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_bsdi) */
+
+
+#if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific items
+ */
+
+/*
+ * There is no Darwin USE_* definition, because lock support in lsof for
+ * Darwin is inadequate for this test.
+ */
+#endif /* defined(LT_DIAL_darwin) */
+
+
+#if defined(LT_DIAL_du)
+/*
+ * DEC_OSF/1|Digital_UNIX|Tru64_UNIX-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_du) */
+
+
+#if defined(LT_DIAL_freebsd)
+/*
+ * FreeBSD-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_freebsd) */
+
+
+#if defined(LT_DIAL_linux)
+/*
+ * Linux-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_linux) */
+
+
+#if defined(LT_DIAL_netbsd)
+/*
+ * NetBSD-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_netbsd) */
+
+
+#if defined(LT_DIAL_openbsd)
+/*
+ * OpenBSD-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_openbsd) */
+
+
+#if defined(LT_DIAL_hpux)
+/*
+ * HP-UX-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_hpux) */
+
+
+#if defined(LT_DIAL_ns)
+/*
+ * NEXTSTEP-specific items
+ */
+
+#define USE_FLOCK
+#endif /* defined(LT_DIAL_ns) */
+
+
+#if defined(LT_DIAL_osr)
+/*
+ * OSR-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_osr) */
+
+
+#if defined(LT_DIAL_ou)
+/*
+ * OpenUNIX-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_ou) */
+
+
+#if defined(LT_DIAL_openbsd)
+/*
+ * OpenBSD-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_openbsd) */
+
+
+#if defined(LT_DIAL_solaris)
+/*
+ * Solaris-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(solaris) */
+
+
+#if defined(LT_DIAL_uw)
+/*
+ * UnixWare-specific items
+ */
+
+#define USE_FCNTL
+#endif /* defined(LT_DIAL_uw) */
+
+
+#if !defined(USE_FLOCK) && !defined(USE_FCNTL)
+/*
+ * Here begins the version of this program for dialects that don't support
+ * flock() or fcntl() locking.
+ */
+
+
+/*
+ * Main program for dialects that don't support flock() of fcntl() locking.
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char *pn; /* program name */
+/*
+ * Get program name and issue error message.
+ */
+ if ((pn = (char *)strrchr(argv[0], '/')))
+ pn++;
+ else
+ pn = argv[0];
+ (void) printf("%s ... %s\n", pn, LT_DONT_DO_TEST);
+ return(0);
+}
+#else /* defined(USE_FLOCK) || defined(USE_FCNTL) */
+
+
+/*
+ * Local definitions
+ */
+
+#define FULL_EX_LOCK 0 /* get a full file exclusive lock */
+#define FULL_SH_LOCK 1 /* get a full file shared lock */
+#define PART_EX_LOCK 2 /* get a partial file exclusive lock */
+#define PART_SH_LOCK 3 /* get a partial file shared lock */
+
+
+/*
+ * Globals
+ */
+
+int Fd = -1; /* test file descriptor; open if >= 0 */
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Path = (char *)NULL; /* test file path; none if NULL */
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *lkfile,(int ty));
+_PROTOTYPE(static char *tstwlsof,(char *opt, char *xlk));
+_PROTOTYPE(static char *unlkfile,(int ty));
+
+
+/*
+ * Main program for dialects that support locking tests.
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ char *em; /* error message pointer */
+ int ti; /* temporary index */
+ char *tcp; /* temporary character pointer */
+ int tlen; /* temporary length -- e.g., as
+ * returned by MkStrCpy() */
+ char *tstR = (char *)NULL; /* "R" lock test result */
+ char *tstr = (char *)NULL; /* "r" lock test result */
+ char *tstW = (char *)NULL; /* "W" lock test result */
+ char *tstw = (char *)NULL; /* "w" lock test result */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ (void) PrtMsg((char *)NULL, Pn);
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "hp:", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg ("usage: [-h] [-p path]", Pn);
+ (void) PrtMsg (" -h print help (this panel)", Pn);
+ (void) PrtMsgX(" -p path define test file path", Pn, cleanup,
+ xv);
+ }
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * If a path was supplied in an "-p path" option, use it. Otherwise construct
+ * a path in the CWD.
+ */
+ if (!(Path = LTopt_p)) {
+ (void) snprintf(buf, sizeof(buf), "./config.LTlock%ld",
+ (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ Path = MkStrCpy(buf, &tlen);
+ }
+/*
+ * Fill buffer for writing to the test file.
+ */
+ for (ti = 0; ti < sizeof(buf); ti++) {
+ buf[ti] = (char)(ti & 0xff);
+ }
+/*
+ * Open a new test file at the specified path.
+ */
+ (void) unlink(Path);
+ if ((Fd = open(Path, O_RDWR|O_CREAT, 0600)) < 0) {
+ (void) fprintf(stderr, "ERROR!!! can't open %s\n", Path);
+
+print_file_error:
+
+ MsgStat = 1;
+ (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s",
+ errno, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Write a buffer load at the beginning of the file.
+ */
+ if (write(Fd, buf, sizeof(buf)) != sizeof(buf)) {
+ (void) fprintf(stderr,
+ "ERROR!!! can't write %d bytes to the beginning of %s\n",
+ (int)sizeof(buf), Path);
+ goto print_file_error;
+ }
+/*
+ * Fsync() the file.
+ */
+ if (fsync(Fd)) {
+ (void) fprintf(stderr, "ERROR!!! can't fsync %s\n", Path);
+ goto print_file_error;
+ }
+/*
+ * Quit (with a hint) if the test file is on an NFS file system.
+ */
+ if (!tstwlsof("-wNa", " ")) {
+ (void) printf("ERROR!!! %s is NFS-mounted.\n", Path);
+ MsgStat = 1;
+ (void) PrtMsg ("Lsof can't report lock information on files that", Pn);
+ (void) PrtMsg ("are located on file systems mounted from a remote", Pn);
+ (void) PrtMsg ("NFS server.\n", Pn);
+ (void) PrtMsg ("Hint: try using \"-p path\" to supply a path in a", Pn);
+ (void) PrtMsg ("non-NFS file system.\n", Pn);
+ (void) PrtMsgX("See 00FAQ and 00TEST for more information.", Pn,
+ cleanup, 1);
+ }
+/*
+ * Get an exclusive lock on the entire file and test it with lsof.
+ */
+ if ((em = lkfile(FULL_EX_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((tstW = tstwlsof("-w", "W")))
+ (void) PrtMsg(tstW, Pn);
+/*
+ * Get a shared lock on the entire file and test it with lsof.
+ */
+ if ((em = unlkfile(FULL_EX_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = lkfile(FULL_SH_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((tstR = tstwlsof("-w", "R")))
+ (void) PrtMsg(tstR, Pn);
+
+# if defined(USE_FLOCK)
+/*
+ * If using flock(), skip the byte lock tests.
+ */
+ tstr = tstw = (char *)NULL;
+# endif /* defined(USE_FLOCK) */
+
+# if defined(USE_FCNTL)
+/*
+ * If using fcntl(), do exclusive and shared byte lock tests,
+ */
+ if ((em = unlkfile(FULL_SH_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = lkfile(PART_EX_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((tstw = tstwlsof("-w", "w")))
+ (void) PrtMsg(tstw, Pn);
+ if ((em = unlkfile(PART_EX_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = lkfile(PART_SH_LOCK)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((tstr = tstwlsof("-w", "r")))
+ (void) PrtMsg(tstr, Pn);
+# endif /* defined(USE_FCNTL) */
+
+/*
+ * Compute exit value and exit.
+ */
+ if (tstr || tstR || tstw || tstW) {
+ tcp = (char *)NULL;
+ xv = 1;
+ } else {
+ tcp = "OK";
+ xv = 0;
+ }
+ (void) PrtMsgX(tcp, Pn, cleanup, xv);
+ return(0);
+}
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+ if (Fd >= 0) {
+ (void) close(Fd);
+ Fd = -1;
+ if (Path) {
+ (void) unlink(Path);
+ Path = (char *)NULL;
+ }
+ }
+}
+
+
+/*
+ * lkfile() -- lock the test file
+ */
+
+static char *
+lkfile(ty)
+ int ty; /* a *_*_LOCK requested */
+{
+ char buf[2048]; /* temporary buffer */
+ int ti; /* temporary integer */
+
+# if defined(USE_FLOCK)
+ int flf; /* flock() function */
+# endif /* defined(USE_FLOCK) */
+
+# if defined(USE_FCNTL)
+ struct flock fl; /* flock control structure */
+/*
+ * Check fcntl() lock request.
+ */
+ (void) memset((void *)&fl, 0, sizeof(fl));
+ switch(ty) {
+ case FULL_EX_LOCK:
+ fl.l_type = F_WRLCK;
+ break;
+ case FULL_SH_LOCK:
+ fl.l_type = F_RDLCK;
+ break;
+ case PART_EX_LOCK:
+ fl.l_type = F_WRLCK;
+ fl.l_len = (off_t)1;
+ break;
+ case PART_SH_LOCK:
+ fl.l_type = F_RDLCK;
+ fl.l_len = (off_t)1;
+ break;
+ default:
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! unknown lock type: %d", ty);
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+/*
+ * Lock test file with fcntl().
+ */
+ if (fcntl(Fd, F_SETLK, &fl) != -1)
+ return((char *)NULL);
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! fcntl() lock error: %s",
+ strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+# endif /* defined(USE_FCNTL) */
+
+# if defined(USE_FLOCK)
+/*
+ * Check flock() lock request.
+ */
+ switch(ty) {
+ case FULL_EX_LOCK:
+ flf = LOCK_EX;
+ break;
+ case FULL_SH_LOCK:
+ flf = LOCK_SH;
+ break;
+ case PART_EX_LOCK:
+ case PART_SH_LOCK:
+ return("ERROR!!! flock() doesn't support partial locks");
+ break;
+ default:
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! unknown flock() type: %d", ty);
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+/*
+ * Acquire lock.
+ */
+ if (!flock(Fd, flf))
+ return((char *)NULL);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! flock() %s lock failed: %s",
+ (flf == LOCK_EX) ? "exclusive" : "shared",
+ strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+# endif /* defined(USE_FLOCK) */
+
+}
+
+
+/*
+ * tstwlsof() -- test the open file with lsof
+ */
+
+static char *
+tstwlsof(opt, xlk)
+ char *opt; /* extra lsof options */
+ char *xlk; /* expected lock value */
+{
+ char buf[2048]; /* temporary buffer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTfldo_t *devp; /* device pointer */
+ char *cem; /* current error message pointer */
+ int ff = 0; /* file found status */
+ LTfldo_t *fop; /* field output pointer */
+ LTfldo_t *inop; /* inode number pointer */
+ LTfldo_t *lkp; /* lock pointer */
+ LTdev_t lsofdc; /* lsof device components */
+ int nf; /* number of fields */
+ LTfldo_t *nmp; /* file name pointer */
+ char *opv[4]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ struct stat sb; /* stat(2) buffer */
+ LTdev_t stdc; /* stat(2) device components */
+ char *tcp; /* temporary character pointer */
+ int ti; /* temporary integer */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Make sure there is an expected lock value.
+ */
+ if (!xlk || !*xlk)
+ (void) PrtMsgX("ERROR!!! no expected lock value", Pn, cleanup, 1);
+/*
+ * Get test file's information.
+ */
+ if (stat(Path, &sb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't stat(2) %s: %s", Path, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Extract components from test file's device number.
+ */
+ if ((cem = ConvStatDev(&sb.st_dev, &stdc)))
+ (void) PrtMsgX(cem, Pn, cleanup, 1);
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ ti = 0;
+ if (opt && *opt)
+ opv[ti++] = opt;
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ opv[ti++] = Path;
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv)))
+ return(cem);
+/*
+ * Read lsof output.
+ */
+ while (!ff && (fop = RdFrLsof(&nf, &cem))) {
+ if (cem) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != MyPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Make sure its number matches the
+ * test file's descriptor number.
+ *
+ * Scan for lock and name fields.
+ */
+ if (!pids)
+ break;
+ for (ti = 0, tcp = fop->v; *tcp; tcp++) {
+
+ /*
+ * Convert file descriptor to a number.
+ */
+ if (*tcp == ' ')
+ continue;
+ if (((int)*tcp < (int)'0') || ((int)*tcp > (int)'9')) {
+ ti = -1;
+ break;
+ }
+ ti = (ti * 10) + (int)*tcp - (int)'0';
+ }
+ if (Fd != ti)
+ break;
+ devp = inop = lkp = nmp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch(fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_LOCK:
+ lkp = fop;
+ break;
+ case LSOF_FID_NAME:
+ nmp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the results of the file descriptor field scan.
+ *
+ * (Don't compare path names because of symbolic link interference.)
+ */
+ if (!devp || !inop || !nmp || !typ)
+ break;
+ if (strcasecmp(typ->v, "reg") && strcasecmp(typ->v, "vreg"))
+ break;
+ if (ConvLsofDev(devp->v, &lsofdc))
+ break;
+ if ((stdc.maj != lsofdc.maj)
+ || (stdc.min != lsofdc.min)
+ || (stdc.unit != lsofdc.unit))
+ break;
+ (void) snprintf(buf, sizeof(buf) - 1, "%u",
+ (unsigned int)sb.st_ino);
+ buf[sizeof(buf) - 1] = '\0';
+ if (strcmp(inop->v, buf))
+ break;
+ /*
+ * The specified file has been located. Check its lock status.
+ */
+ ff = 1;
+ if (!lkp || strcmp(lkp->v, xlk)) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "lock mismatch: expected %s, got \"%s\"", xlk,
+ lkp ? lkp->v : "(none)");
+ pem = MkStrCpy(buf, &ti);
+ }
+ break;
+ }
+ }
+ (void) StopLsof();
+ if (!ff) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "lock test file %s not found by lsof", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+ return(pem);
+}
+
+
+/*
+ * unlkfile() -- unlock the test file
+ */
+
+static char *
+unlkfile(ty)
+ int ty; /* current *_*_LOCK lock typ */
+{
+ char buf[2048]; /* temporary buffer */
+ int ti; /* temporary integer */
+
+# if defined(USE_FCNTL)
+ struct flock fl; /* flock control structure */
+/*
+ * Check current fcntl() lock type.
+ */
+ (void) memset((void *)&fl, 0, sizeof(fl));
+ switch(ty) {
+ case FULL_EX_LOCK:
+ case FULL_SH_LOCK:
+ break;
+ case PART_EX_LOCK:
+ case PART_SH_LOCK:
+ fl.l_len = (off_t)1;
+ break;
+ default:
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! unknown unlock type: %d", ty);
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+/*
+ * Unlock test file with fcntl().
+ */
+ fl.l_type = F_UNLCK;
+ if (fcntl(Fd, F_SETLK, &fl) != -1)
+ return((char *)NULL);
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! fcntl() unlock error: %s",
+ strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+# endif /* defined(USE_FCNTL) */
+
+# if defined(USE_FLOCK)
+/*
+ * Check current flock() lock type.
+ */
+ switch(ty) {
+ case FULL_EX_LOCK:
+ case FULL_SH_LOCK:
+ break;
+ default:
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! unknown unlock type: %s", ty);
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+/*
+ * Unlock file with flock().
+ */
+ if (!flock(Fd, LOCK_UN))
+ return((char *)NULL);
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! flock() unlock error: %s",
+ strerror(errno));
+ return(MkStrCpy(buf, &ti));
+# endif /* defined(USE_FLOCK) */
+
+}
+#endif /* !defined(USE_FLOCK) && !defined(USE_FCNTL) */
diff --git a/tests/LTnfs.c b/tests/LTnfs.c
new file mode 100644
index 0000000..1d20b1b
--- /dev/null
+++ b/tests/LTnfs.c
@@ -0,0 +1,531 @@
+/*
+ * LTnfs.c -- Lsof Test NFS tests
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+
+/*
+ * Pre-definitions that may be revoked by specific dialects
+ */
+
+#define DO_TEST /* do the test */
+
+
+#if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific items
+ */
+
+# if LT_VERS<800
+#undef DO_TEST
+# endif /* LT_VERS<800 */
+#endif /* defined(LT_DIAL_darwin) */
+
+
+/*
+ * Globals
+ */
+
+int Fd = -1; /* test file descriptor; open if >= 0 */
+pid_t MyPid = (pid_t)0; /* PID of this process */
+int NFstat = 0; /* NFS file status: 0 == closed
+ * 1 == not created by this
+ * these and must not be
+ * unlinked
+ * 2 == created by this test
+ * and must be unlinked
+ */
+char *Path = (char *)NULL; /* test file path; none if NULL */
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *FindNFSfile,(int *ff, char *szbuf));
+
+
+/*
+ * Main program
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ char *em; /* error message pointer */
+ int ff; /* FindNFSfile() file-found flag */
+ int sz; /* file size (if created) */
+ char szbuf[32]; /* created test file size in ASCII */
+ int ti; /* temporary index */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+
+#if !defined(DO_TEST)
+/*
+ * If the dialect has disabled the test, echo that result and exit with
+ * a successful return code.
+ */
+ (void) PrtMsgX(LT_DONT_DO_TEST, Pn, cleanup, 0);
+#endif /* !defined(DO_TEST) */
+
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "hp:", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h] [-p path]", Pn);
+ PrtMsg (" -h print help (this panel)", Pn);
+ PrtMsgX (" -p path define test file path", Pn, cleanup, xv);
+ }
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Process the file path and open it.
+ */
+ if ((Path = LTopt_p)) {
+
+ /*
+ * The file path was supplied. Open the file read-only.
+ */
+ if ((Fd = open(Path, O_RDONLY, 0400)) < 0) {
+ (void) fprintf(stderr, "ERROR!!! can't read-only open %s\n",
+ Path);
+ goto print_file_error;
+ }
+ /*
+ * Record that an existing file is being used. Clear its ASCII size.
+ */
+ NFstat = 1;
+ szbuf[0] = '\0';
+ } else {
+
+ /*
+ * The file path wasn't supplied with -p, so generate one.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1, "./config.LTnfs%ld",
+ (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ Path = MkStrCpy(buf, &ti);
+ /*
+ * Open a new test file at the specified path.
+ */
+ (void) unlink(Path);
+ if ((Fd = open(Path, O_RDWR|O_CREAT, 0600)) < 0) {
+ (void) fprintf(stderr, "ERROR!!! can't create %s\n", Path);
+
+print_file_error:
+
+ MsgStat = 1;
+ (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s",
+ errno, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+ NFstat = 2;
+ /*
+ * Write the test file to its expected size.
+ */
+ sz = sizeof(buf);
+ for (ti = 0; ti < sz; ti++) {
+ buf[ti] = (char)(ti & 0xff);
+ }
+ if (write(Fd, buf, sz) != sz) {
+ (void) fprintf(stderr, "ERROR!!! can't write %d bytes to %s\n",
+ sz, Path);
+ goto print_file_error;
+ }
+ /*
+ * Fsync() the file.
+ */
+ if (fsync(Fd)) {
+ (void) fprintf(stderr, "ERROR!!! can't fsync %s\n", Path);
+ goto print_file_error;
+ }
+ /*
+ * Convert the file size to ASCII.
+ */
+ (void) snprintf(szbuf, sizeof(szbuf) - 1, "%d", sz);
+ szbuf[sizeof(szbuf) - 1] = '\0';
+ }
+/*
+ * Make sure the test file can be found on an NFS file system.
+ */
+ if ((em = FindNFSfile(&ff, szbuf))) {
+
+ /*
+ * Print the error message returned by FindNFSfile().
+ */
+ (void) PrtMsg(em, Pn);
+ if (!ff) {
+
+ /*
+ * If the file couldn't be found, print hints.
+ */
+ if (NFstat == 1) {
+ (void) PrtMsg(
+ "Hint: this test must be able to open for read access",
+ Pn);
+ (void) PrtMsg(
+ "the file at the path supplied with the -p option and",
+ Pn);
+ (void) PrtMsg(
+ "that file must be a regular file (not a directory) on",
+ Pn);
+ (void) PrtMsg(
+ "an NFS file system.\n",
+ Pn);
+ (void) PrtMsgX(
+ "See 00FAQ and 00TEST for more information.",
+ Pn, cleanup, 1);
+ } else if (NFstat == 2) {
+ (void) PrtMsg(
+ "Hint: the temporary path generated by this test might",
+ Pn);
+ (void) PrtMsg(
+ "not be on an NFS file system, or this test might be",
+ Pn);
+ (void) PrtMsg(
+ "unable to create a file on the NFS file system.\n",
+ Pn);
+ (void) PrtMsg(
+ "As a work-around use the -p option to specify a path to",
+ Pn);
+ (void) PrtMsg(
+ "a regular file (not a directory) on an NFS file system",
+ Pn);
+ (void) PrtMsg(
+ "to which this test will have read access.\n",
+ Pn);
+ (void) PrtMsgX(
+ "See 00FAQ and 00TEST for more information.",
+ Pn, cleanup, 1);
+ }
+ }
+ }
+/*
+ * Exit successfully.
+ */
+ (void) PrtMsgX("OK", Pn, cleanup, 0);
+ return(0);
+}
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+ if (Fd >= 0) {
+ (void) close(Fd);
+ Fd = -1;
+ if (Path) {
+ if (NFstat == 2)
+ (void) unlink(Path);
+ Path = (char *)NULL;
+ }
+ }
+}
+
+
+/*
+ * FindNFSfile() -- find the NFS file with lsof
+ */
+
+static char *
+FindNFSfile(ff, szbuf)
+ int *ff; /* file-found response receptor */
+ char *szbuf; /* expected file size in ASCII (if
+ * the file was created by this test */
+{
+ char buf[2048]; /* temporary buffer */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTfldo_t *devp; /* device pointer */
+ LTfldo_t *fop; /* field output pointer */
+ char ibuf[64]; /* inode number buffer */
+ LTfldo_t *inop; /* inode number pointer */
+ LTdev_t lsofdc; /* lsof device components */
+ int nf; /* number of fields */
+ char nlkbuf[32]; /* link count buffer */
+ LTfldo_t *nlkp; /* nlink pointer */
+ char *opv[5]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ struct stat sb; /* stat(2) buffer */
+ LTdev_t stdc; /* stat(2) device components */
+ LTfldo_t *szp; /* size pointer */
+ char *tcp; /* temporary character pointer */
+ int ti; /* temporary integer */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Check the argument pointers.
+ *
+ * Set the file-found response false.
+ */
+ if (!ff || !szbuf)
+ (void) PrtMsgX("ERROR!!! missing argument to FindNFSfile()",
+ Pn, cleanup, 1);
+ *ff = 0;
+/*
+ * Get test file's information.
+ */
+ if (stat(Path, &sb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't stat(2) %s: %s", Path, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Extract components from test file's stat buffer.
+ */
+ if ((cem = ConvStatDev(&sb.st_dev, &stdc)))
+ PrtMsgX(cem, Pn, cleanup, 1);
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u", (unsigned int)sb.st_ino);
+ ibuf[sizeof(ibuf) - 1] = '\0';
+ (void) snprintf(nlkbuf, sizeof(nlkbuf) - 1, "%d", (int)sb.st_nlink);
+ nlkbuf[sizeof(nlkbuf) - 1] = '\0';
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ ti = 0;
+ opv[ti++] = "-s";
+ opv[ti++] = "-Na";
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ opv[ti++] = Path;
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv)))
+ return(cem);
+/*
+ * Read lsof output.
+ */
+ while (!*ff && (fop = RdFrLsof(&nf, &cem))) {
+ if (cem) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != MyPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Make sure its number matches the
+ * test file's descriptor number.
+ */
+ if (!pids)
+ break;
+ for (ti = 0, tcp = fop->v; *tcp; tcp++) {
+
+ /*
+ * Convert file descriptor to a number.
+ */
+ if (*tcp == ' ')
+ continue;
+ if (((int)*tcp < (int)'0') || ((int)*tcp > (int)'9')) {
+ ti = -1;
+ break;
+ }
+ ti = (ti * 10) + (int)*tcp - (int)'0';
+ }
+ if (Fd != ti)
+ break;
+ /*
+ * Scan for device, inode, nlink, offset, size and type fields.
+ */
+ devp = inop = nlkp, szp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_NLINK:
+ nlkp = fop;
+ break;
+ case LSOF_FID_OFFSET:
+ break;
+ case LSOF_FID_SIZE:
+ szp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the device, inode, and type of the file.
+ */
+ if (!devp || !inop || !typ)
+ break;
+ if (strcasecmp(typ->v, "reg") && strcasecmp(typ->v, "vreg"))
+ break;
+ if ((cem = ConvLsofDev(devp->v, &lsofdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if ((stdc.maj != lsofdc.maj)
+ || (stdc.min != lsofdc.min)
+ || (stdc.unit != lsofdc.unit)
+ || strcmp(inop->v, ibuf)
+ ) {
+ break;
+ }
+ /*
+ * Indicate the file was found.
+ */
+ *ff = 1;
+ /*
+ * Check the link count.
+ */
+ if (!nlkp) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! lsof didn't report a link count for %s", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if (strcmp(nlkp->v, nlkbuf)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! wrong link count: expected %s, got %s",
+ nlkbuf, nlkp->v);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ /*
+ * If the file was created by this test, check its size.
+ */
+ if (NFstat == 2) {
+ if (!szp) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! lsof didn't report a size for %s", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if (strcmp(szp->v, szbuf)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! wrong file size: expected %s, got %s",
+ szbuf, szp->v);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ }
+ /*
+ * The requested file was located. Return the previous error message
+ * pointer. (It will be NULL if no error was detected.)
+ */
+ (void) StopLsof();
+ return(pem);
+ }
+ }
+/*
+ * The test file wasn't found.
+ */
+ (void) StopLsof();
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! test file %s not found by lsof", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+}
diff --git a/tests/LTnlink.c b/tests/LTnlink.c
new file mode 100644
index 0000000..7f12e86
--- /dev/null
+++ b/tests/LTnlink.c
@@ -0,0 +1,586 @@
+/*
+ * LTnlink.c -- Lsof Test nlink tests
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+
+/*
+ * Pre-definitions that may be changed by specific dialects
+ */
+
+#define DO_TEST /* do the test */
+
+
+/*
+ * Dialect-specific items
+ */
+
+
+#if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific items
+ */
+
+# if defined(LT_KMEM)
+#undef DO_TEST
+# endif /* defined(LT_KMEM) */
+
+#endif /* defined(LT_DIAL_darwin) */
+
+/*
+ * Globals
+ */
+
+int Fd = -1; /* test file descriptor; open if >= 0 */
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Path = (char *)NULL; /* test file path; none if NULL */
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *FindFile,(char *opt, int *ff, int ie, LTdev_t *tfdc,
+ char *ibuf, char *xlnk, char *szbuf));
+
+
+/*
+ * Main program
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ int do_unlink = 1; /* do the unlink test section */
+ char *em; /* error message pointer */
+ int ff; /* FindFile() file-found flag */
+ char ibuf[32]; /* inode number in ASCII */
+ char *opt; /* lsof option */
+ int sz; /* file size */
+ char szbuf[32]; /* file size in ASCII */
+ LTdev_t tfdc; /* device components */
+ struct stat tfsb; /* test file stat(2) buffer */
+ int ti, tj; /* temporary indexes */
+ char xlnk[32]; /* expected link count in ASCII */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+
+#if !defined(DO_TEST)
+/*
+ * Quit if lsof for this dialect doesn't support adequate nlink reporting.
+ */
+ (void) PrtMsgX(LT_DONT_DO_TEST, Pn, cleanup, 0);
+#endif /* !defined(DO_TEST) */
+
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "hp:", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h] [-p path]", Pn);
+ PrtMsg (" -h print help (this panel)", Pn);
+ PrtMsgX (" -p path define test file path", Pn, cleanup, xv);
+ }
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Process the file path.
+ */
+ if (!(Path = LTopt_p)) {
+
+ /*
+ * The file path was not supplied, so make one.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1, "./config.LTnlink%ld",
+ (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ Path = MkStrCpy(buf, &ti);
+ }
+/*
+ * Create the test file.
+ */
+ (void) unlink(Path);
+ if ((Fd = open(Path, O_RDWR|O_CREAT, 0600)) < 0) {
+ (void) fprintf(stderr, "ERROR!!! can't create %s\n", Path);
+
+print_file_error:
+
+ MsgStat = 1;
+ (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s",
+ errno, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Write the test file to its expected size.
+ */
+ sz = sizeof(buf);
+ for (ti = 0; ti < sz; ti++) {
+ buf[ti] = (char)(ti & 0xff);
+ }
+ if (write(Fd, buf, sz) != sz) {
+ (void) fprintf(stderr, "ERROR!!! can't write %d bytes to %s\n",
+ sz, Path);
+ goto print_file_error;
+ }
+/*
+ * Fsync() the file.
+ */
+ if (fsync(Fd)) {
+ (void) fprintf(stderr, "ERROR!!! can't fsync %s\n", Path);
+ goto print_file_error;
+ }
+/*
+ * Stat(2) the test file.
+ */
+ if (stat(Path, &tfsb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't stat(2) %s: %s", Path, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Set the test file status to open and linked.
+ *
+ * Get the test file's parameters:
+ *
+ * * device paramters in LTdev_t form;
+ * * inode number in ASCII;
+ * * link count in ASCII;
+ * * file size in ASCII.
+ */
+ if ((em = ConvStatDev(&tfsb.st_dev, &tfdc)))
+ PrtMsgX(em, Pn, cleanup, 1);
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u", (unsigned int)tfsb.st_ino);
+ ibuf[sizeof(szbuf) - 1] = '\0';
+ (void) snprintf(xlnk, sizeof(xlnk) - 1, "%d", (int)tfsb.st_nlink);
+ ibuf[sizeof(szbuf) - 1] = '\0';
+ (void) snprintf(szbuf, sizeof(szbuf) - 1, "%d", sz);
+ szbuf[sizeof(szbuf) - 1] = '\0';
+/*
+ * See if the file is on an NFS file system.
+ */
+ (void) FindFile("-Na", &ff, 1, &tfdc, ibuf, xlnk, szbuf);
+ if (ff) {
+
+ /*
+ * The file was found on an NFS file system.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "WARNING!!! Test file %s is NFS mounted.", Path);
+ (void) PrtMsg(buf, Pn);
+ (void) PrtMsg(
+ " As a result this test probably won't be able to unlink it and",
+ Pn);
+ (void) PrtMsg(
+ " find its open and unlinked instance with lsof's +L option.",
+ Pn);
+ (void) PrtMsg(
+ " Therefore, that section of this test has been disabled.\n",
+ Pn);
+ (void) PrtMsg(
+ " Hint: supply a path with the -p option to a file in a non-NFS",
+ Pn);
+ (void) PrtMsg(
+ " file system that this test can write and unlink.\n",
+ Pn);
+ (void) PrtMsg(
+ " See 00FAQ and 00TEST for more information.",
+ Pn);
+ do_unlink = 0;
+ }
+/*
+ * Find the test file.
+ */
+ if ((em = FindFile("+L", &ff, 0, &tfdc, ibuf, xlnk, szbuf)))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * If the unlink test is enabled, do it.
+ */
+ if (do_unlink) {
+ if (unlink(Path)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! unlink(%s) failed: (%s).", Path, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ " %s may be on a ZFS file system, where it", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ " is not possible for %s to unlink the file it has open.", Pn);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ " To run the %s test, use the \"-p path\" option to specify",
+ Pn);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) PrtMsg(
+ " a file on a file system -- e.g., UFS -- that supports unlink",
+ Pn);
+ (void) PrtMsg(
+ " while the file is open. Usually /tmp can do that -- e.g.,",
+ Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ " run the test as \"./%s -p /tmp/<name>\".\n", Pn);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsg(buf, Pn);
+ (void) PrtMsgX( " See 00FAQ and 00TEST for more information.",
+ Pn, cleanup, 1);
+ }
+ for (opt = "+L1", ti = 0, tj = 30; ti < tj; ti++) {
+
+ /*
+ * Wait a while for the link count to be updated before concluding
+ * lsof can't find the unlinked file. Use "+L1" for only the first
+ * third of the tries, then switch to "+L".
+ */
+ if ((ti + ti + ti) >= tj)
+ opt = "+L";
+ if (!(em = FindFile(opt, &ff, 0, &tfdc, ibuf, "0", szbuf)))
+ break;
+ if (ti)
+ (void) printf(".");
+ else
+ (void) printf("waiting for link count update: .");
+ (void) fflush(stdout);
+ (void) sleep(2);
+ }
+ if (ti) {
+
+ /*
+ * End the delay message.
+ */
+ printf("\n");
+ (void) fflush(stdout);
+ MsgStat = 1;
+ }
+ if (em)
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ }
+/*
+ * Exit successfully.
+ */
+ (void) PrtMsgX("OK", Pn, cleanup, 0);
+ return(0);
+}
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+ if (Fd >= 0) {
+ (void) close(Fd);
+ Fd = -1;
+ }
+ if (Path)
+ (void) unlink(Path);
+}
+
+
+/*
+ * FindFile() -- find a file with lsof
+ */
+
+static char *
+FindFile(opt, ff, ie, tfdc, ibuf, xlnk, szbuf)
+ char *opt; /* additional lsof options */
+ int *ff; /* file-found response receptor */
+ int ie; /* ignore errors if == 1 */
+ LTdev_t *tfdc; /* test file device components */
+ char *ibuf; /* inode number in ASCII */
+ char *xlnk; /* expected link count */
+ char *szbuf; /* file size in ASCII */
+{
+ char buf[2048]; /* temporary buffer */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTfldo_t *devp; /* device pointer */
+ LTfldo_t *fop; /* field output pointer */
+ LTfldo_t *inop; /* inode number pointer */
+ LTdev_t lsofdc; /* lsof device components */
+ int nf; /* number of fields */
+ LTfldo_t *nlkp; /* nlink pointer */
+ char *opv[4]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ LTfldo_t *szp; /* size pointer */
+ char *tcp; /* temporary character pointer */
+ int ti; /* temporary integer */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Check the argument pointers.
+ *
+ * Set the file-found response false.
+ */
+ if (!ff || !ibuf || !szbuf || !tfdc || !xlnk)
+ (void) PrtMsgX("ERROR!!! missing argument to FindFile()",
+ Pn, cleanup, 1);
+ *ff = 0;
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ ti = 0;
+ if (opt && *opt)
+ opv[ti++] = opt;
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ if (strcmp(xlnk, "0"))
+ opv[ti++] = Path;
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv))) {
+ if (ie)
+ return((char *)NULL);
+ return(cem);
+ }
+/*
+ * Read lsof output.
+ */
+ while (!*ff && (fop = RdFrLsof(&nf, &cem))) {
+ if (cem) {
+ if (ie)
+ return((char *)NULL);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != MyPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Make sure its number matches the
+ * test file's descriptor number.
+ */
+ if (!pids)
+ break;
+ for (ti = 0, tcp = fop->v; *tcp; tcp++) {
+
+ /*
+ * Convert file descriptor to a number.
+ */
+ if (*tcp == ' ')
+ continue;
+ if (((int)*tcp < (int)'0') || ((int)*tcp > (int)'9')) {
+ ti = -1;
+ break;
+ }
+ ti = (ti * 10) + (int)*tcp - (int)'0';
+ }
+ if (Fd != ti)
+ break;
+ /*
+ * Scan for device, inode, nlink, size and type fields.
+ */
+ devp = inop = nlkp = szp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_NLINK:
+ nlkp = fop;
+ break;
+ case LSOF_FID_SIZE:
+ szp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the device, inode, and type of the file.
+ */
+ if (!devp || !inop || !szp || !typ)
+ break;
+ if (strcasecmp(typ->v, "reg") && strcasecmp(typ->v, "vreg"))
+ break;
+ if ((cem = ConvLsofDev(devp->v, &lsofdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if ((tfdc->maj != lsofdc.maj)
+ || (tfdc->min != lsofdc.min)
+ || (tfdc->unit != lsofdc.unit)
+ || strcmp(inop->v, ibuf)
+ ) {
+ break;
+ }
+ /*
+ * Indicate the file was found.
+ */
+ *ff = 1;
+ /*
+ * Check the size and link count.
+ */
+ if (!szp) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! lsof didn't report a file size for %s", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if (strcmp(szp->v, szbuf)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! wrong file size: expected %s, got %s",
+ szbuf, szp->v);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if (!nlkp) {
+ if (strcmp(xlnk, "0")) {
+
+ /*
+ * If lsof returned no link count and the expected return is
+ * not "0", it's an error. Otherwise, interpret no link count
+ * as equivalent to a "0" link count.
+ */
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! lsof didn't report a link count for %s",
+ Path);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ } else {
+ if (strcmp(nlkp->v, xlnk)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! wrong link count: expected %s, got %s",
+ xlnk, nlkp->v);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ }
+ /*
+ * The requested file was located. Return the previous error message
+ * pointer unless errors are being ignored. (The previous error
+ * message pointer will be NULL if no error was detected.)
+ */
+ (void) StopLsof();
+ if (ie)
+ return((char *)NULL);
+ return(pem);
+ }
+ }
+/*
+ * Clean up and return.
+ */
+ (void) StopLsof();
+ if (!*ff && !ie) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! %s test file %s not found by lsof",
+ strcmp(xlnk, "0") ? "linked" : "unlinked",
+ Path);
+ buf[sizeof(buf) - 1] = '\0';
+ pem = MkStrCpy(buf, &ti);
+ }
+ if (ie)
+ return((char *)NULL);
+ return(pem);
+}
diff --git a/tests/LTsock.c b/tests/LTsock.c
new file mode 100644
index 0000000..bc0750f
--- /dev/null
+++ b/tests/LTsock.c
@@ -0,0 +1,886 @@
+/*
+ * LTsock.c -- Lsof Test IPv4 sockets
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+#include <netdb.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+/*
+ * Pre-definitions that make be changed or revoked by dialects
+ */
+
+#define SIGHANDLER_T void /* signal handler function type */
+#define LT_SOCKLEN_T int /* socket length type */
+
+
+#if defined(LT_DIAL_aix)
+/*
+ * AIX-specific items
+ */
+
+#undef LT_SOCKLEN_T
+#define LT_SOCKLEN_T size_t
+#endif /* defined(LT_DIAL_aix) */
+
+
+#if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific items
+ */
+
+# if LT_VERS>=800
+#undef LT_SOCKLEN_T
+#define LT_SOCKLEN_T socklen_t
+# endif /* LT_VERS>=800 */
+#endif /* defined(LT_DIAL_darwin) */
+
+
+#if defined(LT_DIAL_freebsd)
+/*
+ * FreeBSD-specific items
+ */
+#undef LT_SOCKLEN_T
+#define LT_SOCKLEN_T socklen_t
+#endif /* defined(LT_DIAL_freebsd) */
+
+
+#if defined(LT_DIAL_hpux)
+/*
+ * HP-UX-specific items
+ */
+
+# if LT_VERS>=1123 && defined(__GNUC__)
+#undef LT_SOCKLEN_T
+#define LT_SOCKLEN_T size_t
+# endif /* LT_VERS>=1123 && defined(__GNUC__) */
+#endif /* defined(LT_DIAL_hpux) */
+
+
+#if defined(LT_DIAL_ou)
+/*
+ * OpenUNIX-specific items
+ */
+
+#undef LT_SOCKLEN_T
+#define LT_SOCKLEN_T size_t
+#endif /* defined(LT_DIAL_ou) */
+
+
+#if defined(LT_DIAL_uw)
+/*
+ * UnixWare-specific items
+ */
+
+#undef LT_SOCKLEN_T
+#define LT_SOCKLEN_T size_t
+#endif /* defined(LT_DIAL_uw) */
+
+
+/*
+ * Local definitions
+ */
+
+#define ALARMTM 30 /* alarm timer */
+
+#define LT_CLNT 0 /* child process index */
+#define LT_SRVR 1 /* parent process index */
+
+#define LT_FNF 0 /* file not found */
+#define LT_FBYIP 1 /* file found by IP address */
+#define LT_FBYHN 2 /* file found by host name */
+#define LT_FBYPORT 4 /* file found by port */
+
+#if !defined(MAXHOSTNAMELEN)
+#define MAXHOSTNAMELEN 256 /* maximum host name length */
+#endif /* !defined(MAXHOSTNAMELEN) */
+
+#if !defined(MAXPATHLEN)
+#define MAXPATHLEN 1024 /* maximum path length */
+#endif /* !defined(MAXPATHLEN) */
+
+
+/*
+ * Local structure definitions.
+ */
+
+
+typedef struct fdpara { /* file descriptor parameters */
+ int fd; /* FD */
+ char *fds; /* FD in ASCII */
+ int ff; /* file found flags (see LT_F*) */
+ char *host; /* host name */
+ int hlen; /* strlen(host) */
+ char *ipaddr; /* dotted IP address */
+ int ilen; /* strlen(ipaddr) */
+ pid_t pid; /* PID of process */
+ char *port; /* port in ASCII */
+ int plen; /* strlen(port) */
+ struct sockaddr_in sa; /* socket's address */
+} fdpara_t;
+
+
+/*
+ * Globals
+ */
+
+pid_t CPid = (pid_t)0; /* client PID */
+fdpara_t FdPara[2]; /* file descriptor parameters */
+#define NFDPARA (sizeof(FdPara) /sizeof(fdpara_t))
+struct sockaddr_in Myad; /* my (server) socket address */
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Pn = (char *)NULL; /* program name */
+char *PtNm[] = { "client", "server" };
+ /* program type name */
+int Ssock = -1; /* server socket */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void CleanupClnt,(void));
+_PROTOTYPE(static void CleanupSrvr,(void));
+_PROTOTYPE(static SIGHANDLER_T HandleClntAlarm,(int sig));
+_PROTOTYPE(static SIGHANDLER_T HandleSrvrAlarm,(int sig));
+_PROTOTYPE(static char *FindSock,(int fn));
+_PROTOTYPE(static void StartClnt,(struct sockaddr_in *cad));
+
+
+/*
+ * Main program
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ struct sockaddr_in aa; /* accept address */
+ struct sockaddr_in ba; /* bind address */
+ char buf[2048]; /* temporary buffer */
+ int bufl = sizeof(buf); /* size of buf[] */
+ struct sockaddr_in ca; /* connect address */
+ char *cem; /* current error message pointer */
+ char *ep; /* error message parameter */
+ char hnm[MAXHOSTNAMELEN + 1]; /* this host's name */
+ char *host; /* host name */
+ struct hostent *hp; /* this host's hostent structure */
+ char *ipaddr; /* IP address */
+ char *pem = (char *)NULL; /* previous error message */
+ char *port; /* port */
+ LT_SOCKLEN_T sal; /* socket address length */
+ char *tcp; /* temporary character size */
+ int ti, tj, tk; /* temporary indexes */
+ int tsfd; /* temporary socket FD */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+/*
+ * Initalize the FdPara[] array before any CleanupClnt() call.
+ */
+ for (ti = 0; ti < NFDPARA; ti++) {
+ (void) memset((void *)&FdPara[ti], 0, sizeof(fdpara_t));
+ FdPara[ti].fd = -1;
+ FdPara[ti].ff = LT_FNF;
+ }
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "h", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h]", Pn);
+ PrtMsgX(" -h print help (this panel)", Pn, CleanupSrvr,
+ xv);
+ }
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((cem = IsLsofExec()))
+ (void) PrtMsgX(cem, Pn, CleanupSrvr, 1);
+ if ((cem = CanRdKmem()))
+ (void) PrtMsgX(cem, Pn, CleanupSrvr, 1);
+/*
+ * Get the host name and its IP address. Convert the IP address to dotted
+ * ASCII form.
+ */
+ if (gethostname(hnm, sizeof(hnm) - 1)) {
+ cem = "ERROR!!! can't get this host's name";
+ goto print_errno;
+ }
+ hnm[sizeof(hnm) - 1] = '\0';
+ if (!(hp = gethostbyname(hnm))) {
+ (void) snprintf(buf, bufl - 1, "ERROR!!! can't get IP address for %s",
+ hnm);
+ buf[bufl - 1] = '\0';
+ cem = buf;
+ goto print_errno;
+ }
+ (void) memset((void *)&Myad, 0, sizeof(Myad));
+ if ((ti = hp->h_length) > sizeof(Myad.sin_addr))
+ ti = sizeof(Myad.sin_addr);
+ (void) memcpy((void *)&Myad.sin_addr, (void *)hp->h_addr, ti);
+ Myad.sin_family = hp->h_addrtype;
+/*
+ * Get INET domain socket FDs.
+ */
+ for (ti = 0; ti < NFDPARA; ti++) {
+ if ((tsfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ ep = "socket";
+
+print_errno_by_ti:
+
+ /*
+ * Report socket function error.
+ *
+ * Entry: ep = function name
+ * hnm = this host's name
+ * Myad = this host's IP address
+ * ti = FdPara[] index
+ */
+ (void) snprintf(buf, bufl - 1, "ERROR!!! %s %s() failure",
+ PtNm[ti], ep);
+ buf[bufl - 1] = '\0';
+ PrtMsg(buf, Pn);
+ (void) snprintf(buf, bufl - 1, " host: %s",
+ FdPara[ti].host ? FdPara[ti].host : hnm);
+ buf[bufl - 1] = '\0';
+ PrtMsg(buf, Pn);
+ (void) snprintf(buf, bufl - 1, " IP: %s",
+ FdPara[ti].ipaddr ? FdPara[ti].ipaddr
+ : inet_ntoa(Myad.sin_addr));
+ buf[bufl - 1] = '\0';
+ cem = buf;
+
+print_errno:
+
+ /*
+ * Report errno.
+ *
+ * Entry: errno = error number
+ */
+ PrtMsg(cem, Pn);
+ (void) snprintf(buf, bufl - 1, " Errno %d: %s", errno,
+ strerror(errno));
+ buf[bufl - 1] = '\0';
+ PrtMsgX(buf, Pn, CleanupSrvr, 1);
+ }
+ /*
+ * Put the FD just acquired in FdPara[ti].fd.
+ *
+ * Set the file-not-found to LT_FNF.
+ *
+ * Save the server socket if this FdPara[] is for it.
+ */
+ FdPara[ti].fd = tsfd;
+ (void) snprintf(buf, bufl - 1, "%d", tsfd);
+ buf[bufl - 1] = '\0';
+ FdPara[ti].fds = MkStrCpy(buf, &tj);
+ if (ti == LT_SRVR)
+ Ssock = tsfd;
+ }
+/*
+ * Bind the host name to the server socket.
+ *
+ * Get and save the server's socket address.
+ *
+ * Initiate a listen with an address list of one.
+ */
+ (void) memcpy((void *)&ba, (void *)&Myad, sizeof(ba));
+ ti = LT_SRVR;
+ FdPara[ti].pid = MyPid;
+ if (bind(Ssock, (struct sockaddr *)&ba, sizeof(ba)) < 0) {
+ ep = "bind";
+ goto print_errno_by_ti;
+ }
+ sal = (LT_SOCKLEN_T)sizeof(ca);
+ if (getsockname(Ssock, (struct sockaddr *)&ca, &sal)) {
+ ep = "getsockname";
+ goto print_errno_by_ti;
+ }
+ (void) memcpy((void *)&FdPara[ti].sa, (void *)&ca, sizeof(FdPara[ti].sa));
+ if (listen(Ssock, 1) < 0) {
+ ep = "listen";
+ goto print_errno_by_ti;
+ }
+/*
+ * Fork a child process to run as the client.
+ */
+ switch ((CPid = (pid_t)fork())) {
+ case (pid_t)0:
+
+ /*
+ * This is the child. Start the client.
+ */
+ StartClnt(&ca);
+ (void) PrtMsgX("ERROR!!! unexpected client return", Pn, CleanupSrvr,
+ 1);
+ case (pid_t)-1:
+
+ /*
+ * This is a fork error.
+ */
+ cem = "ERROR!!! fork() error";
+ goto print_errno;
+ default:
+
+ /*
+ * This is the parent.
+ *
+ * Save the client's PID.
+ *
+ * Close the client's socket.
+ */
+ FdPara[LT_CLNT].pid = CPid;
+ if (FdPara[LT_CLNT].fd >= 0) {
+ (void) close(FdPara[LT_CLNT].fd);
+ FdPara[LT_CLNT].fd = -1;
+ }
+ }
+/*
+ * Set a SIGALRM, then accept() the connection from the client.
+ *
+ * Save the client's socket address.
+ *
+ * Replace the server's FD with the accepted one and close the original.
+ */
+ sal = (LT_SOCKLEN_T)sizeof(aa);
+ (void) alarm(0);
+ (void) signal(SIGALRM, HandleSrvrAlarm);
+ (void) alarm(ALARMTM);
+ tsfd = FdPara[LT_SRVR].fd = accept(Ssock, (struct sockaddr *)&aa, &sal);
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ if (tsfd < 0) {
+ ep = "accept";
+ goto print_errno_by_ti;
+ }
+ (void) snprintf(buf, bufl - 1, "%d", tsfd);
+ buf[bufl - 1] = '\0';
+ if (FdPara[LT_SRVR].fds)
+ (void) free((void *)FdPara[LT_SRVR].fds);
+ FdPara[LT_SRVR].fds = MkStrCpy(buf, &tj);
+ ti = LT_CLNT;
+ (void) memcpy((void *)&FdPara[ti].sa, (void *)&aa, sizeof(FdPara[ti].sa));
+ (void) close(Ssock);
+ Ssock = -1;
+/*
+ * Convert the client and server IP address to ASCII form.
+ *
+ * Look up the client and server host names for their IP addresses.
+ *
+ * Convert the port from the socket address to host form.
+ */
+ for (ti = 0; ti < NFDPARA; ti++) {
+ tcp = inet_ntoa(FdPara[ti].sa.sin_addr);
+ FdPara[ti].ipaddr = MkStrCpy(tcp, &FdPara[ti].ilen);
+ (void) snprintf(buf, bufl - 1, "%d",
+ (int)ntohs(FdPara[ti].sa.sin_port));
+ buf[bufl - 1] = '\0';
+ FdPara[ti].port = MkStrCpy(buf, &FdPara[ti].plen);
+ if (!(hp = gethostbyaddr((char *)&FdPara[ti].sa.sin_addr,
+ sizeof(FdPara[ti].sa.sin_addr),
+ FdPara[ti].sa.sin_family))
+ ) {
+ ep = "gethostbyaddr";
+ goto print_errno_by_ti;
+ }
+ if (hp->h_name)
+ FdPara[ti].host = MkStrCpy(hp->h_name, &FdPara[ti].hlen);
+ else {
+
+ /*
+ * The connected client's socket address can't be mapped to a host
+ * name.
+ */
+
+ (void) snprintf(buf, bufl - 1,
+ "ERROR!!! can't map %s (client) to a host name",
+ FdPara[ti].ipaddr);
+ buf[bufl - 1] = '\0';
+ PrtMsgX(buf, Pn, CleanupSrvr, 1);
+ }
+ }
+/*
+ * Call lsof three times to find the two sockets: 1) by host name and port;
+ * 2) by IP address and port; and 3) by port.
+ */
+ if ((cem = FindSock(LT_FBYHN)))
+ PrtMsgX(cem, Pn, CleanupSrvr, 1);
+ if ((cem = FindSock(LT_FBYIP)))
+ PrtMsgX(cem, Pn, CleanupSrvr, 1);
+ if ((cem = FindSock(LT_FBYPORT)))
+ PrtMsgX(cem, Pn, CleanupSrvr, 1);
+/*
+ * Check the FindSock() results.
+ */
+ for (pem = (char *)NULL, ti = 0; ti < NFDPARA; ti++) {
+ if ((tj = FdPara[ti].ff) != (LT_FBYHN | LT_FBYIP | LT_FBYPORT)) {
+ host = FdPara[ti].host;
+ ipaddr = FdPara[ti].ipaddr;
+ port = FdPara[ti].port;
+
+ /*
+ * This FD wasn't found by some search method.
+ */
+ if (!(tj & LT_FBYHN)) {
+
+ /*
+ * The search by host name and port failed.
+ */
+ (void) snprintf(buf, bufl - 1,
+ "ERROR!!! no %s socket by host and port: %s@%s",
+ PtNm[ti], host, port);
+ buf[bufl - 1] = '\0';
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = MkStrCpy(buf, &tk);
+ }
+ if (!(tj & LT_FBYIP)) {
+
+ /*
+ * The search by IP address and port failed.
+ */
+ (void) snprintf(buf, bufl - 1,
+ "ERROR!!! no %s socket by IP and port: %s@%s",
+ PtNm[ti], ipaddr, port);
+ buf[bufl - 1] = '\0';
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = MkStrCpy(buf, &tk);
+ }
+ if (!(tj & LT_FBYPORT)) {
+
+ /*
+ * The search by port number failed.
+ */
+ (void) snprintf(buf, bufl - 1,
+ "ERROR!!! no %s socket by port: %s",
+ PtNm[ti], port);
+ buf[bufl - 1] = '\0';
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = MkStrCpy(buf, &tk);
+ }
+ }
+ }
+ if (pem)
+ (void) PrtMsgX(pem, Pn, CleanupSrvr, 1);
+/*
+ * Exit successfully.
+ */
+ (void) PrtMsgX("OK", Pn, CleanupSrvr, 0);
+ return(0);
+}
+
+
+/*
+ * ClntCleanup() -- release client resources
+ */
+
+static void
+CleanupClnt()
+{
+ int tfd; /* temporary file descriptor */
+
+ if ((tfd = FdPara[LT_CLNT].fd) >= 0) {
+ (void) shutdown(tfd, 2);
+ (void) close(tfd);
+ FdPara[LT_CLNT].fd = -1;
+ }
+}
+
+
+/*
+ * CleanupSrvr() -- release server resources
+ */
+
+static void
+CleanupSrvr()
+{
+ int tfd; /* temporary file descriptor */
+ int ti; /* temporary index */
+ pid_t wpid; /* wait() PID */
+
+ if ((Ssock >= 0) && (Ssock != FdPara[LT_SRVR].fd)) {
+ (void) shutdown(Ssock, 2);
+ (void) close(Ssock);
+ Ssock = -1;
+ }
+ for (ti = 0; ti < NFDPARA; ti++) {
+ if ((tfd = FdPara[ti].fd) >= 0) {
+ (void) shutdown(tfd, 2);
+ (void) close(tfd);
+ FdPara[ti].fd = -1;
+ }
+ }
+ if (CPid > 0) {
+ wpid = wait3(NULL, WNOHANG, NULL);
+ if (wpid != CPid) {
+ kill(CPid, SIGKILL);
+ (void) wait3(NULL, WNOHANG, NULL);
+ }
+ CPid = (pid_t)0;
+ }
+}
+
+
+/*
+ * FindSock() -- find sockets with lsof
+ */
+
+static char *
+FindSock(fn)
+ int fn; /* function -- an LT_FBY* value */
+{
+ char buf[2048]; /* temporary buffer */
+ int bufl = sizeof(buf); /* size of buf[] */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTfldo_t *fop; /* field output pointer */
+ int nf; /* number of fields */
+ int nl; /* name length */
+ LTfldo_t *nmp; /* name pointer */
+ char *opv[5]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ int pl; /* port length */
+ int px; /* process index -- LT_CLNT or
+ * LT_SRVR */
+ char *tcp, *tcp1; /* temporary character pointers */
+ int ti, tj; /* temporary integers */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Check the function and determine the first lsof option from it.
+ */
+ ti = 0;
+ switch (fn) {
+ case LT_FBYHN:
+ opv[ti++] = "-P";
+ for (tj = 0; tj < NFDPARA; tj++) {
+ (void) snprintf(buf, bufl - 1, "-i@%s:%s", FdPara[tj].host,
+ FdPara[tj].port);
+ buf[bufl - 1] = '\0';
+ opv[ti++] = MkStrCpy(buf, &pl);
+ }
+ break;
+ case LT_FBYIP:
+ opv[ti++] = "-Pn";
+ for (tj = 0; tj < NFDPARA; tj++) {
+ (void) snprintf(buf, bufl - 1, "-i@%s:%s", FdPara[tj].ipaddr,
+ FdPara[tj].port);
+ buf[bufl - 1] = '\0';
+ opv[ti++] = MkStrCpy(buf, &pl);
+ }
+ break;
+ case LT_FBYPORT:
+ opv[ti++] = "-P";
+ for (tj = 0; tj < NFDPARA; tj++) {
+ (void) snprintf(buf, bufl - 1, "-i:%s", FdPara[tj].port);
+ buf[bufl - 1] = '\0';
+ opv[ti++] = MkStrCpy(buf, &pl);
+ }
+ break;
+ default:
+ (void) snprintf(buf, bufl - 1,
+ "ERROR!!! illegal FindSock() function: %d", fn);
+ buf[bufl - 1] = '\0';
+ return(MkStrCpy(buf, &ti));
+ }
+/*
+ * Complete the option vector and start lsof execution.
+ */
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv)))
+ return(cem);
+/*
+ * Read lsof output.
+ */
+ while ((((FdPara[LT_CLNT].ff & fn) == 0)
+ || ((FdPara[LT_SRVR].ff & fn) == 0))
+ && (fop = RdFrLsof(&nf, &cem))
+ ) {
+ if (cem) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || ((pid != CPid) && (pid != MyPid)))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line.
+ *
+ * Identify the process -- client or server.
+ */
+ if (!pids)
+ break;
+ if (pid == CPid)
+ px = LT_CLNT;
+ else if (pid == MyPid)
+ px = LT_SRVR;
+ else
+ break;
+ /*
+ * Make sure the FD matches the identified process.
+ */
+ if (strcmp(fop->v, FdPara[px].fds))
+ break;
+ /*
+ * Scan for name and type.
+ */
+ nmp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_NAME:
+ nmp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the type of the file.
+ */
+ if (!typ
+ || (strcasecmp(typ->v, "inet") && strcasecmp(typ->v, "ipv4"))
+ ) {
+ break;
+ }
+ /*
+ * Check the addess in the name, based on the calling function.
+ */
+ if (!nmp)
+ break;
+ tcp = nmp->v;
+ switch (fn) {
+ case LT_FBYHN:
+ if (((nl = FdPara[px].hlen) <= 0)
+ || !(tcp1 = FdPara[px].host)
+ || strncasecmp(tcp, tcp1, nl)
+ ) {
+ break;
+ }
+ tcp += nl;
+ if ((*tcp++ != ':')
+ || !(tcp1 = FdPara[px].port)
+ || ((pl = FdPara[px].plen) <= 0)
+ || strncmp(tcp, tcp1, pl)
+ ) {
+ break;
+ }
+ tcp += pl;
+ if ((*tcp == '-') || (*tcp == ' ') || !*tcp) {
+ FdPara[px].ff |= LT_FBYHN;
+ }
+ break;
+ case LT_FBYIP:
+ if (((nl = FdPara[px].ilen) <= 0)
+ || !(tcp1 = FdPara[px].ipaddr)
+ || strncasecmp(tcp, tcp1, nl)
+ ) {
+ break;
+ }
+ tcp += nl;
+ if ((*tcp++ != ':')
+ || !(tcp1 = FdPara[px].port)
+ || ((pl = FdPara[px].plen) <= 0)
+ || strncmp(tcp, tcp1, pl)
+ ) {
+ break;
+ }
+ tcp += pl;
+ if ((*tcp == '-') || (*tcp == ' ') || !*tcp) {
+ FdPara[px].ff |= LT_FBYIP;
+ }
+ break;
+ case LT_FBYPORT:
+ if (!(tcp = strchr(tcp, ':')))
+ break;
+ tcp++;
+ if (!(tcp1 = FdPara[px].port)
+ || ((pl = FdPara[px].plen) <= 0)
+ || strncmp(tcp, tcp1, pl)
+ ) {
+ break;
+ }
+ tcp += pl;
+ if ((*tcp == '-') || (*tcp == ' ') || !*tcp) {
+ FdPara[px].ff |= LT_FBYPORT;
+ }
+ break;
+ }
+ }
+ }
+/*
+ * Clean up and return.
+ */
+ (void) StopLsof();
+ return(pem);
+}
+
+
+/*
+ * HandleClntAlarm() -- handle client alarm
+ */
+
+static SIGHANDLER_T
+HandleClntAlarm(sig)
+ int sig; /* the signal (SIGALRM) */
+{
+ (void) PrtMsgX("ERROR!!! client caught an alarm signal", Pn,
+ CleanupClnt, 1);
+}
+
+
+/*
+ * Handle SrvrAlarm() -- handle server alarm
+ */
+
+static SIGHANDLER_T
+HandleSrvrAlarm(sig)
+ int sig; /* the signal (SIGALRM) */
+{
+ (void) PrtMsgX("ERROR!!! server caught an alarm signal.", Pn,
+ CleanupSrvr, 1);
+}
+
+
+/*
+ * StartClnt() -- start network client
+ */
+
+static void
+StartClnt(cad)
+ struct sockaddr_in *cad; /* connection address */
+{
+ struct sockaddr_in ba; /* bind address */
+ int br; /* bytes read */
+ char buf[2048]; /* temporary buffer */
+ int bufl = sizeof(buf); /* size of buf[] */
+ int cr; /* connect() reply */
+ char *em; /* error message pointer */
+ int fd = FdPara[LT_CLNT].fd; /* client's socket FD */
+/*
+ * Close the server's sockets.
+ */
+ if ((Ssock >= 0) && (Ssock != FdPara[LT_SRVR].fd)) {
+ (void) close(Ssock);
+ Ssock = -1;
+ }
+ if (FdPara[LT_SRVR].fd >= 0) {
+ (void) close(FdPara[LT_SRVR].fd);
+ FdPara[LT_SRVR].fd = -1;
+ }
+/*
+ * Bind to the local address.
+ */
+ (void) memcpy((void *)&ba, (void *)&Myad, sizeof(ba));
+ if (bind(fd, (struct sockaddr *)&ba, sizeof(ba)) < 0) {
+ em = "bind";
+
+client_errno:
+
+ (void) snprintf(buf, bufl - 1,
+ "ERROR!!! client %s error: %s", em, strerror(errno));
+ buf[bufl - 1] = '\0';
+ (void) PrtMsgX(em, Pn, CleanupClnt, 1);
+ }
+/*
+ * Set an alarm timeout and connect to the server.
+ */
+ (void) signal(SIGALRM, HandleClntAlarm);
+ (void) alarm(ALARMTM);
+ cr = connect(fd, (struct sockaddr *)cad, sizeof(struct sockaddr_in));
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ if (cr) {
+ em = "connect";
+ goto client_errno;
+ }
+/*
+ * Sleep until the socket closes or the parent kills the process.
+ */
+ for (br = 0; br >= 0;) {
+ sleep(1);
+ br = read(fd, buf, bufl);
+ }
+ (void) CleanupClnt();
+ exit(0);
+}
diff --git a/tests/LTszoff.c b/tests/LTszoff.c
new file mode 100644
index 0000000..090e752
--- /dev/null
+++ b/tests/LTszoff.c
@@ -0,0 +1,509 @@
+/*
+ * LTszoff.c -- Lsof Test small file (< 32 bits) size and offset tests
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+
+/*
+ * Pre-definitions that might be undefined by dialects
+ */
+
+#define OFFTST_STAT 1 /* offset tests status */
+
+
+#if defined(LT_DIAL_linux)
+/*
+ * Linux-specific items
+ */
+
+#undef OFFTST_STAT
+#define OFFTST_STAT 0 /* Linux lsof may not be able to report
+ * offsets -- see the function
+ * ck_Linux_offset_support() */
+
+_PROTOTYPE(static int ck_Linux_offset_support,(void));
+#endif /* defined(LT_DIAL_linux) */
+
+
+/*
+ * Local definitions
+ */
+
+#define TYTST_SZ 0 /* size test type */
+#define TYTST_0to 1 /* 0t offset test type */
+#define TYTST_0xo 2 /* 0x offset test type */
+#define TSTFSZ 32768 /* test file size */
+
+
+/*
+ * Globals
+ */
+
+int Fd = -1; /* test file descriptor; open if >= 0 */
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Path = (char *)NULL; /* test file path; none if NULL */
+char *Pn = (char *)NULL; /* program name */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *testlsof,(int tt, char *opt, char *xval));
+
+
+/*
+ * Main program
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ int do_offt = OFFTST_STAT; /* do offset tests if == 1 */
+ char *em; /* error message pointer */
+ int ti; /* temporary index */
+ char *tcp; /* temporary character pointer */
+ char *tstsz = (char *)NULL; /* size test status */
+ char *tst0to = (char *)NULL; /* offset 0t form test */
+ char *tst0xo = (char *)NULL; /* offset 0x form test */
+ int xv = 0; /* exit value */
+ char xbuf[64]; /* expected value buffer */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "hp:", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h] [-p path]", Pn);
+ PrtMsg (" -h print help (this panel)", Pn);
+ PrtMsgX (" -p path define test file path", Pn, cleanup, xv);
+ }
+
+#if defined(LT_DIAL_linux)
+/*
+ * If this is Linux, see if lsof can report file offsets.
+ */
+ do_offt = ck_Linux_offset_support();
+#endif /* defined(LT_DIAL_linux) */
+
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * If a path was supplied in an "-p path" option, use it. Otherwise construct
+ * a path in the CWD.
+ */
+ if (!(Path = LTopt_p)) {
+ (void) snprintf(buf, sizeof(buf) - 1, "./config.LTszoff%ld",
+ (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ Path = MkStrCpy(buf, &ti);
+ }
+/*
+ * Open a new test file at the specified path.
+ */
+ (void) unlink(Path);
+ if ((Fd = open(Path, O_RDWR|O_CREAT, 0600)) < 0) {
+ (void) fprintf(stderr, "ERROR!!! can't open %s\n", Path);
+
+print_file_error:
+
+ MsgStat = 1;
+ (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s",
+ errno, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Write the test file to its expected size.
+ */
+ for (ti = 0; ti < sizeof(buf); ti++) {
+ buf[ti] = (char)(ti & 0xff);
+ }
+ for (ti = 0; ti < TSTFSZ; ti += sizeof(buf)) {
+ if (write(Fd, buf, sizeof(buf)) != sizeof(buf)) {
+ (void) fprintf(stderr, "ERROR!!! can't write %d bytes to %s\n",
+ (int)sizeof(buf), Path);
+ goto print_file_error;
+ }
+ }
+/*
+ * Fsync() the file.
+ */
+ if (fsync(Fd)) {
+ (void) fprintf(stderr, "ERROR!!! can't fsync %s\n", Path);
+ goto print_file_error;
+ }
+/*
+ * Do the tests. Skip offset tests as indicated.
+ */
+ (void) snprintf(xbuf, sizeof(xbuf) - 1, "%d", TSTFSZ);
+ xbuf[sizeof(xbuf) - 1] = '\0';
+ if ((tstsz = testlsof(TYTST_SZ, "-s", xbuf)))
+ (void) PrtMsg(tstsz, Pn);
+ if (do_offt) {
+ (void) snprintf(xbuf, sizeof(xbuf) - 1, "0t%d", TSTFSZ);
+ xbuf[sizeof(xbuf) - 1] = '\0';
+ if ((tst0to = testlsof(TYTST_0to, "-o", xbuf)))
+ (void) PrtMsg(tst0to, Pn);
+ (void) snprintf(xbuf, sizeof(xbuf) - 1, "0x%x", TSTFSZ);
+ xbuf[sizeof(xbuf) - 1] = '\0';
+ if ((tst0xo = testlsof(TYTST_0xo, "-oo2", xbuf)))
+ (void) PrtMsg(tst0to, Pn);
+ } else {
+ PrtMsg("WARNING!!! lsof can't return file offsets for this dialect,",
+ Pn);
+ PrtMsg(" so offset tests have been disabled.", Pn);
+ }
+/*
+ * Compute exit value and exit.
+ */
+ if (tstsz || tst0to || tst0xo) {
+ tcp = (char *)NULL;
+ xv = 1;
+ } else {
+ tcp = "OK";
+ xv = 0;
+ }
+ (void) PrtMsgX(tcp, Pn, cleanup, xv);
+ return(0);
+}
+
+
+#if defined(LT_DIAL_linux)
+/*
+ * ck_Linux_offset_support() -- see if lsof can report offsets for this
+ * Linux implementation
+ */
+
+static int
+ck_Linux_offset_support()
+{
+ char buf[1024]; /* lsof output line buffer */
+ int bufl = sizeof(buf); /* size of buf[] */
+ char *opv[5]; /* option vector for lsof */
+ int rv = 1; /* return value:
+ * 0 == no lsof offset support
+ * 1 == lsof offset support */
+/*
+ * Ask lsof to report the test's FD zero offset.
+ */
+ if (IsLsofExec())
+ return(0);
+ opv[0] = "-o";
+ snprintf(buf, bufl - 1, "-p%d", (int)getpid());
+ opv[1] = buf;
+ opv[2] = "-ad0";
+ opv[3] = "+w";
+ opv[4] = (char *)NULL;
+ if (ExecLsof(opv))
+ return(0);
+/*
+ * Read the lsof output. Look for a line with "WARNING: can't report offset"
+ * in it. If it is found, then this Linux lsof can't report offsets.
+ */
+ while(fgets(buf, bufl - 1, LsofFs)) {
+ if (strstr(buf, "WARNING: can't report offset")) {
+ rv = 0;
+ break;
+ }
+ }
+ (void) StopLsof();
+ return(rv);
+}
+#endif /* defined(LT_DIAL_linux) */
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+ if (Fd >= 0) {
+ (void) close(Fd);
+ Fd = -1;
+ if (Path) {
+ (void) unlink(Path);
+ Path = (char *)NULL;
+ }
+ }
+}
+
+
+/*
+ * testlsof() -- test the open file with lsof
+ */
+
+static char *
+testlsof(tt, opt, xval)
+ int tt; /* test type -- TYTST_* symbol */
+ char *opt; /* extra lsof options */
+ char *xval; /* expected value */
+{
+ char buf[2048]; /* temporary buffer */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ LTfldo_t *devp; /* device pointer */
+ int ff = 0; /* file found status */
+ LTfldo_t *fop; /* field output pointer */
+ char ibuf[64]; /* inode number buffer */
+ LTfldo_t *inop; /* inode number pointer */
+ LTdev_t lsofdc; /* lsof device components */
+ int nf; /* number of fields */
+ LTfldo_t *offp; /* offset pointer */
+ char *opv[4]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ struct stat sb; /* stat(2) buffer */
+ LTdev_t stdc; /* stat(2) device components */
+ LTfldo_t *szp; /* size pointer */
+ char *tcp; /* temporary character pointer */
+ int ti; /* temporary integer */
+ char *tnm1, *tnm2; /* test names */
+ int ts = 0; /* test status flag */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Check the test type.
+ */
+ switch (tt) {
+ case TYTST_SZ:
+ tnm1 = "";
+ tnm2 = " size";
+ break;
+ case TYTST_0to:
+ tnm1 = " 0t";
+ tnm2 = " offset";
+ break;
+ case TYTST_0xo:
+ tnm1 = " 0x";
+ tnm2 = " offset";
+ break;
+ default:
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! illegal test type: %d", tt);
+ buf[sizeof(buf) - 1] = '\0';
+ (void) PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Get test file's information.
+ */
+ if (stat(Path, &sb)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! can't stat(2) %s: %s", Path, strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ PrtMsgX(buf, Pn, cleanup, 1);
+ }
+/*
+ * Extract components from test file's stat buffer.
+ */
+ if ((cem = ConvStatDev(&sb.st_dev, &stdc)))
+ PrtMsgX(buf, Pn, cleanup, 1);
+ (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u", (unsigned int)sb.st_ino);
+ ibuf[sizeof(ibuf) - 1] = '\0';
+/*
+ * Complete the option vector and start lsof execution.
+ */
+ ti = 0;
+ if (opt && *opt)
+ opv[ti++] = opt;
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#else /* !defined(USE_LSOF_C_OPT) */
+ opv[ti++] = "--";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ opv[ti++] = Path;
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv)))
+ return(cem);
+/*
+ * Read lsof output.
+ */
+ while (!ff && !cem && (fop = RdFrLsof(&nf, &cem))) {
+ if (cem) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != MyPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Make sure its number matches the
+ * test file's descriptor number.
+ */
+ if (!pids)
+ break;
+ for (ti = 0, tcp = fop->v; *tcp; tcp++) {
+
+ /*
+ * Convert file descriptor to a number.
+ */
+ if (*tcp == ' ')
+ continue;
+ if (((int)*tcp < (int)'0') || ((int)*tcp > (int)'9')) {
+ ti = -1;
+ break;
+ }
+ ti = (ti * 10) + (int)*tcp - (int)'0';
+ }
+ if (Fd != ti)
+ break;
+ /*
+ * Scan for device, inode, offset, size and type fields.
+ */
+ devp = inop = offp = szp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_DEVN:
+ devp = fop;
+ break;
+ case LSOF_FID_INODE:
+ inop = fop;
+ break;
+ case LSOF_FID_OFFSET:
+ offp = fop;
+ break;
+ case LSOF_FID_SIZE:
+ szp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the results of the file descriptor field scan.
+ */
+ if (!devp || !inop || !typ)
+ break;
+ if (strcasecmp(typ->v, "reg") && strcasecmp(typ->v, "vreg"))
+ break;
+ if ((cem = ConvLsofDev(devp->v, &lsofdc))) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ break;
+ }
+ if ((stdc.maj != lsofdc.maj)
+ || (stdc.min != lsofdc.min)
+ || (stdc.unit != lsofdc.unit)
+ || strcmp(inop->v, ibuf)
+ ) {
+ break;
+ }
+ /*
+ * The specified file has been located. Do the specified test.
+ */
+ ff = 1;
+ fop = (tt == TYTST_SZ) ? szp : offp;
+ if (!fop) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! %s%s test, but no lsof%s", tnm1, tnm2, tnm2);
+ ts = 1;
+ } else if (strcmp(fop->v, xval)) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! %s%s mismatch: expected %s, got %s",
+ tnm1, tnm2, xval, fop->v);
+ ts = 1;
+ }
+ if (ts) {
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = cem;
+ }
+ break;
+ }
+ }
+ (void) StopLsof();
+ if (!ff) {
+ (void) snprintf(buf, sizeof(buf) - 1,
+ "ERROR!!! test file %s not found by lsof", Path);
+ buf[sizeof(buf) - 1] = '\0';
+ cem = MkStrCpy(buf, &ti);
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ return(pem);
+}
diff --git a/tests/LTunix.c b/tests/LTunix.c
new file mode 100644
index 0000000..6e12c33
--- /dev/null
+++ b/tests/LTunix.c
@@ -0,0 +1,364 @@
+/*
+ * LTunix.c -- Lsof Test UNIX domain socket test
+ *
+ * V. Abell
+ * Purdue University
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by V. Abell.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
+#endif
+
+#include "LsofTest.h"
+#include "lsof_fields.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+
+/*
+ * Local definitions
+ */
+
+#if !defined(MAXPATHLEN)
+#define MAXPATHLEN 1024 /* maximum path length */
+#endif /* !defined(MAXPATHLEN) */
+
+
+/*
+ * Globals
+ */
+
+pid_t MyPid = (pid_t)0; /* PID of this process */
+char *Pn = (char *)NULL; /* program name */
+int SpFd[2] = {-1,-1}; /* socket pair FDs */
+char *Path[2] = {(char *)NULL, (char *)NULL};
+ /* socket pair paths */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void cleanup,(void));
+_PROTOTYPE(static char *FindUsocks,(void));
+
+
+/*
+ * Main program
+ */
+
+int
+main(argc, argv)
+ int argc; /* argument count */
+ char *argv[]; /* arguments */
+{
+ char buf[2048]; /* temporary buffer */
+ char cwd[MAXPATHLEN + 1]; /* CWD buffer */
+ char *em; /* error message pointer */
+ int ti, tj; /* temporary indexes */
+ struct sockaddr_un ua; /* UNIX socket address */
+ int xv = 0; /* exit value */
+/*
+ * Get program name and PID, issue start message, and build space prefix.
+ */
+ if ((Pn = strrchr(argv[0], '/')))
+ Pn++;
+ else
+ Pn = argv[0];
+ MyPid = getpid();
+ (void) printf("%s ... ", Pn);
+ (void) fflush(stdout);
+ PrtMsg((char *)NULL, Pn);
+/*
+ * Process arguments.
+ */
+ if (ScanArg(argc, argv, "h", Pn))
+ xv = 1;
+ if (xv || LTopt_h) {
+ (void) PrtMsg("usage: [-h]", Pn);
+ PrtMsgX(" -h print help (this panel)", Pn, cleanup, xv);
+ }
+/*
+ * See if lsof can be executed and can access kernel memory.
+ */
+ if ((em = IsLsofExec()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+ if ((em = CanRdKmem()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Construct the socket paths.
+ */
+
+#if defined(USE_GETCWD)
+ if (!getcwd(cwd, sizeof(cwd)))
+#else /* ! defined(USE_GETCWD) */
+ if (!getwd(cwd))
+#endif /* defined(USE_GETCWD) */
+
+ {
+ em = "ERROR!!! can't get CWD";
+ goto print_errno;
+ }
+ cwd[sizeof(cwd) - 1] = '\0';
+ if ((strlen(cwd) + strlen("/config.LT#U9223372036854775807") + 1)
+ > sizeof(ua.sun_path))
+ {
+ strncpy(cwd, "/tmp", sizeof(cwd) - 1);
+ }
+ for (ti = 0; ti < 2; ti++) {
+ (void) snprintf(buf, sizeof(buf) - 1, "%s/config.LT%dU%ld", cwd, ti,
+ (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ Path[ti] = MkStrCpy(buf, &tj);
+ (void) unlink(Path[ti]);
+ }
+/*
+ * Get two UNIX domain socket FDs.
+ */
+ for (ti = 0; ti < 2; ti++) {
+ if ((SpFd[ti] = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC)) < 0) {
+ em = "socket";
+
+print_errno_by_ti:
+
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! %s(%s) failure",
+ em, Path[ti]);
+ buf[sizeof(buf) - 1] = '\0';
+ em = buf;
+
+print_errno:
+
+ PrtMsg(em, Pn);
+ (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s", errno,
+ strerror(errno));
+ buf[sizeof(buf) - 1] = '\0';
+ PrtMsgX(buf, Pn, cleanup, 1);
+ }
+ }
+/*
+ * Bind file system names to the sockets.
+ */
+ for (ti = 0; ti < 2; ti++) {
+ (void) memset((void *)&ua, 0, sizeof(ua));
+ ua.sun_family = AF_UNIX;
+ (void) strncpy(ua.sun_path, Path[ti], sizeof(ua.sun_path));
+ ua.sun_path[sizeof(ua.sun_path) - 1] = '\0';
+ if (bind(SpFd[ti], (struct sockaddr *)&ua, sizeof(ua)) < 0) {
+ em = "bind";
+ goto print_errno_by_ti;
+ }
+ }
+/*
+ * Look for the open UNIX domain socket files with lsof.
+ */
+ if ((em = FindUsocks()))
+ (void) PrtMsgX(em, Pn, cleanup, 1);
+/*
+ * Exit successfully.
+ */
+ (void) PrtMsgX("OK", Pn, cleanup, 0);
+ return(0);
+}
+
+
+/*
+ * cleanup() -- release resources
+ */
+
+static void
+cleanup()
+{
+ int ti;
+
+ for (ti = 0; ti < 2; ti++) {
+ if (SpFd[ti] >= 0) {
+ (void) close(SpFd[ti]);
+ SpFd[ti] = -1;
+ }
+ if (Path[ti]) {
+ (void) unlink(Path[ti]);
+ (void) free((void *)Path[ti]);
+ Path[ti] = (char *)NULL;
+ }
+ }
+}
+
+
+/*
+ * FindUsocks() -- find UNIX sockets with lsof
+ */
+
+static char *
+FindUsocks()
+{
+ char buf[2048]; /* temporary buffer */
+ char *cem; /* current error message pointer */
+ LTfldo_t *cmdp; /* command pointer */
+ int ff[2]; /* file-found flags */
+ LTfldo_t *fop; /* field output pointer */
+ int nf; /* number of fields */
+ int nl; /* name length */
+ LTfldo_t *nmp; /* name pointer */
+ char *opv[5]; /* option vector for ExecLsof() */
+ char *pem = (char *)NULL; /* previous error message pointer */
+ pid_t pid; /* PID */
+ int pids = 0; /* PID found status */
+ char *tcp; /* temporary character pointer */
+ int ti, tj; /* temporary integers */
+ LTfldo_t *typ; /* file type pointer */
+/*
+ * Build the option vector and start lsof execution.
+ */
+ ff[0] = ff[1] = ti = 0;
+ opv[ti++] = "-aU";
+ opv[ti++] = "-p";
+ (void) snprintf(buf, sizeof(buf) - 1, "%ld", (long)MyPid);
+ buf[sizeof(buf) - 1] = '\0';
+ opv[ti++] = MkStrCpy(buf, &tj);
+
+#if defined(USE_LSOF_C_OPT)
+ opv[ti++] = "-C";
+#endif /* defined(USE_LSOF_C_OPT) */
+
+ opv[ti] = (char *)NULL;
+ if ((cem = ExecLsof(opv)))
+ return(cem);
+/*
+ * Read lsof output.
+ */
+ while (((ff[0] + ff[1]) < 2) && (fop = RdFrLsof(&nf, &cem))) {
+ if (cem) {
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ return(cem);
+ }
+ switch (fop->ft) {
+ case LSOF_FID_PID:
+
+ /*
+ * This is a process information line.
+ */
+ pid = (pid_t)atoi(fop->v);
+ pids = 1;
+ cmdp = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_CMD:
+ cmdp = fop;
+ break;
+ }
+ }
+ if (!cmdp || (pid != MyPid))
+ pids = 0;
+ break;
+ case LSOF_FID_FD:
+
+ /*
+ * This is a file descriptor line. Make sure its number matches a
+ * test file descriptor number.
+ */
+ if (!pids)
+ break;
+ for (ti = 0, tcp = fop->v; *tcp; tcp++) {
+
+ /*
+ * Convert file descriptor to a number.
+ */
+ if (*tcp == ' ')
+ continue;
+ if (((int)*tcp < (int)'0') || ((int)*tcp > (int)'9')) {
+ ti = -1;
+ break;
+ }
+ ti = (ti * 10) + (int)*tcp - (int)'0';
+ }
+ for (tj = 0; tj < 2; tj++) {
+ if (ff[tj])
+ continue;
+ if (SpFd[tj] == ti)
+ break;
+ }
+ if (tj >= 2)
+ break;
+ /*
+ * Scan for name and type.
+ */
+ nmp = typ = (LTfldo_t *)NULL;
+ for (fop++, ti = 1; ti < nf; fop++, ti++) {
+ switch (fop->ft) {
+ case LSOF_FID_NAME:
+ nmp = fop;
+ break;
+ case LSOF_FID_TYPE:
+ typ = fop;
+ break;
+ }
+ }
+ /*
+ * Check the type of the file.
+ */
+ if (!typ || strcasecmp(typ->v, "unix"))
+ break;
+ /*
+ * Look for the name.
+ */
+ if (!nmp)
+ break;
+ nl = strlen(Path[tj]);
+ for (tcp = nmp->v; tcp; tcp = strchr(tcp + 1, '/')) {
+ if (!strncmp(tcp, Path[tj], nl)) {
+
+ /*
+ * Mark a file as found.
+ */
+ ff[tj] = 1;
+ break;
+ }
+ }
+ }
+ }
+/*
+ * Clean up and return.
+ */
+ (void) StopLsof();
+ for (ti = 0; ti < 2; ti++) {
+ if (ff[tj])
+ continue;
+ (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! not found: %s",
+ Path[ti]);
+ buf[sizeof(buf) - 1] = '\0';
+ if (pem)
+ (void) PrtMsg(pem, Pn);
+ pem = MkStrCpy(buf, &tj);
+ }
+ return(pem);
+}
diff --git a/tests/LsofTest.h b/tests/LsofTest.h
new file mode 100644
index 0000000..e1d149c
--- /dev/null
+++ b/tests/LsofTest.h
@@ -0,0 +1,360 @@
+/*
+ * LsofTest.h -- header file for lsof tests
+ */
+
+
+/*
+ * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by Victor A. Abell
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+
+/*
+ * $Id: LsofTest.h,v 1.12 2008/07/05 16:21:07 abe Exp $
+ */
+
+
+#if !defined(LSOF_TEST_H)
+#define LSOF_TEST_H 1
+
+
+/*
+ * The _PROTOTYPE macro provides strict ANSI C prototypes if __STDC__
+ * is defined, and old-style K&R prototypes otherwise.
+ *
+ * (With thanks to Andy Tanenbaum)
+ */
+
+# if defined(__STDC__)
+#define _PROTOTYPE(function, params) function params
+# else /* !defined(__STDC__) */
+#define _PROTOTYPE(function, params) function()
+# endif /* defined(__STDC__) */
+
+
+/*
+ * The following define keeps gcc>=2.7 from complaining about the failure
+ * of the Exit() function to return.
+ *
+ * Paul Eggert <eggert@twinsun.com> supplied it.
+ */
+
+# if defined(__GNUC__) && !(__GNUC__<2 || (__GNUC__==2 && __GNUC_MINOR__<7))
+#define exiting __attribute__((__noreturn__))
+# else /* !gcc || gcc<2.7 */
+#define exiting
+# endif /* gcc && gcc>=2.7 */
+
+
+/*
+ * Necessary header files.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+
+/*
+ * Definitions that may be revoked by a particular dialect.
+ */
+
+#define USE_GETCWD /* use the POSIX getcwd() function in
+ * place of getwd() */
+#define USE_LSOF_C_OPT /* use lsof's -C option */
+#undef USE_LSOF_X_OPT /* don't use lsof's -X option */
+
+
+# if defined(LT_DIAL_aix)
+/*
+ * AIX-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/access.h>
+#undef USE_LSOF_C_OPT
+#define USE_LSOF_X_OPT
+# endif /* defined(LT_DIAL_aix) */
+
+
+# if defined(LT_DIAL_bsdi)
+/*
+ * OpenBSD-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+# endif /* defined(LT_DIAL_bsdi) */
+
+
+# if defined(LT_DIAL_darwin)
+/*
+ * Darwin-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#undef USE_LSOF_C_OPT
+# endif /* defined(LT_DIAL_darwin) */
+
+
+# if defined(LT_DIAL_du)
+/*
+ * DEC_OSF/1|Digital_UNIX|Tru64_UNIX-specific items
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+# if LT_VERS<50000
+#define snprintf snpf /* use lsof's snpf() */
+# endif /* LT_VERS<50000 */
+# endif /* defined(LT_DIAL_du) */
+
+
+# if defined(LT_DIAL_freebsd)
+/*
+ * FreeBSD-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+# endif /* defined(LT_DIAL_freebsd) */
+
+
+# if defined(LT_DIAL_linux)
+/*
+ * Linux-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#undef USE_LSOF_C_OPT
+# endif /* defined(LT_DIAL_linux) */
+
+
+# if defined(LT_DIAL_hpux)
+/*
+ * HP-UX-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+# endif /* defined(LT_DIAL_hpux) */
+
+
+# if defined(LT_DIAL_netbsd)
+/*
+ * NetBSD-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+# endif /* defined(LT_DIAL_netbsd) */
+
+
+# if defined(LT_DIAL_openbsd)
+/*
+ * OpenBSD-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+# endif /* defined(LT_DIAL_openbsd) */
+
+
+# if defined(LT_DIAL_ou)
+/*
+ * OpenUNIX-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+# endif /* defined(LT_DIAL_ou) */
+
+
+# if defined(LT_DIAL_osr)
+/*
+ * OSR-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+# endif /* defined(LT_DIAL_osr) */
+
+
+# if defined(LT_DIAL_ns)
+/*
+ * NEXTSTEP-specific items
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+typedef int pid_t;
+#define snprintf snpf
+
+#undef USE_GETCWD
+# endif /* defined(LT_DIAL_ns) */
+
+
+# if defined(LT_DIAL_solaris)
+/*
+ * Solaris-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+# if defined(LT_VPATH)
+#undef USE_LSOF_C_OPT
+#endif /* defined(LT_VPATH) */
+# endif /* defined(LT_DIAL_solaris) */
+
+
+# if defined(LT_DIAL_uw)
+/*
+ * UnixWare-specific items
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+# endif /* defined(LT_DIAL_uw) */
+
+
+/*
+ * Local definitions, including ones may have been left undefined by
+ * dialect-specific header files
+ */
+
+#define LT_DONT_DO_TEST "this test does not run on this dialect."
+#define LT_DEF_LSOF_PATH "../lsof"
+
+# if !defined(MAXPATHLEN)
+#define MAXPATHLEN 1024
+# endif /* !defined(MAXPATHLEN) */
+
+
+/*
+ * Local structure definitions
+ */
+
+typedef struct LTdev { /* local device parameters */
+ unsigned int maj; /* major device number */
+ unsigned int min; /* minor device number */
+ unsigned int unit; /* unit number (where applicable) */
+} LTdev_t;
+
+typedef struct LTfldo { /* lsof field output information */
+ char ft; /* field identifier (see the LSOF_FID_*
+ * definitions in ../lsof_fields.h) */
+ char *v; /* field value character string */
+} LTfldo_t;
+#define LT_FLDO_ALLOC 16 /* LTfldo_t allocation increment */
+
+
+/*
+ * Lsof test library global variable external declarations:
+ *
+ * these global variables may be found in LTlib.c.
+ */
+
+extern int LsofFd; /* lsof pipe FD */
+extern FILE *LsofFs; /* stream for lsof pipe FD */
+extern char *LsofPath; /* path to lsof executable */
+extern pid_t LsofPid; /* PID of lsof child process */
+extern int LTopt_h; /* "-h" option's switch value */
+extern char *LTopt_p; /* "-p path" option's path value */
+extern int MsgStat; /* message status */
+
+
+/*
+ * External declarations
+ */
+
+extern int errno; /* error number */
+
+
+/*
+ * Lsof test library function prototypes:
+ *
+ * these functions may be found in LTlib.c.
+ */
+
+_PROTOTYPE(extern char *CanRdKmem,(void));
+_PROTOTYPE(extern char *ConvStatDev,(dev_t *dev, LTdev_t *ldev));
+_PROTOTYPE(extern char *ConvLsofDev,(char *dev, LTdev_t *ldev));
+_PROTOTYPE(extern char *ExecLsof,(char **opt));
+_PROTOTYPE(extern char *IsLsofExec,(void));
+_PROTOTYPE(extern void LTlibClean,(void));
+_PROTOTYPE(extern char *MkStrCpy,(char *src, int *len));
+_PROTOTYPE(extern LTfldo_t *RdFrLsof,(int *nf, char **em));
+_PROTOTYPE(extern void PrtMsg,(char *mp, char *pn));
+_PROTOTYPE(extern void PrtMsgX,(char *mp, char *pn, void (*f)(), int xv));
+_PROTOTYPE(extern int ScanArg,(int ac, char *av[], char *opt, char *pn));
+_PROTOTYPE(extern void StopLsof,(void));
+
+#endif /* LSOF_TEST_H */
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..08574a0
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,159 @@
+# Makefile for testing lsof
+#
+# V. Abell
+# Purdue University
+#
+# $Id: Makefile,v 1.17 2005/05/17 00:40:53 abe Exp abe $
+
+DEBUG=
+CFLAGS= ${DEBUG} -I. -I..
+
+HDR= LsofTest.h
+
+CKTSTDB= CkTestDB
+CONFCFL= ./config.cflags
+CONFIG= ./config.cc ${CONFCFL} ./config.xobj
+LTOBJ= LTlib.o
+LTSRC= LTlib.c
+LIBOBJ= ${LTOBJ}
+
+BASTST= LTbasic
+STDTST= LTnlink LTsock LTszoff LTunix
+OPTTST= LTbigf LTdnlc LTlock LTnfs
+
+all: ${CKTSTDB} ${BASTST} ${STDTST} FRC
+ @./${CKTSTDB}; xv=$$?; \
+ if [ $$xv -ne 0 ]; then \
+ exit 1 ;\
+ fi
+ @rm -f config.LT*
+ -@err=0; \
+ echo ""; \
+ echo "Basic test:"; \
+ ./${BASTST}; \
+ if [ $$? -ne 0 ]; then \
+ exit 1; \
+ fi; \
+ echo ""; \
+ echo "Standard tests:"; \
+ for i in ${STDTST}; do \
+ ./$$i; \
+ if [ $$? -ne 0 ]; then \
+ err=`expr $$err + 1`; \
+ fi; \
+ done; \
+ if [ $$err -ne 0 ]; then \
+ echo "Failed tests: $$err"; \
+ echo ""; \
+ echo "See 00FAQ and 00TEST for more information."; \
+ else \
+ echo "All standard tests succeeded."; \
+ echo ""; \
+ grep LT_DIAL_darwin ${CONFCFL} > /dev/null 2>&1; \
+ if [ $$? -ne 0 ]; then \
+ echo "Suggestion: try the optional tests: \"make opt\""; \
+ echo ""; \
+ fi; \
+ fi;
+ @rm -f config.LT*
+
+auto: ckDB silent FRC
+
+ckDB: ${CKTSTDB} FRC
+ @echo "" | ./${CKTSTDB}; xv=$$?; \
+ if [ $$xv -ne 0 ]; then \
+ exit 1 ;\
+ fi
+
+clean: FRC
+ rm -f ${BASTST} ${STDTST} ${OPTTST} *.o *.err *.out config.LT*
+
+FRC:
+
+LTbasic: LTbasic.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTbasic.c \
+ ${LIBOBJ} `cat config.xobj` -o LTbasic
+
+LTbigf: LTbigf.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTbigf.c \
+ ${LIBOBJ} `cat config.xobj` -o LTbigf
+
+LTdnlc: LTdnlc.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTdnlc.c \
+ ${LIBOBJ} `cat config.xobj` -o LTdnlc
+
+LTlock: LTlock.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTlock.c \
+ ${LIBOBJ} `cat config.xobj` -o LTlock
+
+${LTOBJ}: ${HDR} ${LTSRC} config.cflags config.cc
+ `cat config.cc` ${CFLAGS} `cat config.cflags` -c ${LTSRC} \
+ -o ${LTOBJ}
+
+LTnfs: LTnfs.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTnfs.c \
+ ${LIBOBJ} `cat config.xobj` -o LTnfs
+
+LTnlink: LTnlink.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTnlink.c \
+ ${LIBOBJ} `cat config.xobj` -o LTnlink
+
+LTsock: LTsock.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTsock.c \
+ ${LIBOBJ} `cat config.xobj` -o LTsock `cat config.ldflags`
+
+LTszoff: LTszoff.c ${CONFIG} ${LIBOBJ} ${HDR}
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTszoff.c \
+ ${LIBOBJ} `cat config.xobj` -o LTszoff
+
+LTunix: LTunix.c ${CONFIG} ${LIBOBJ} ${HDR} config.ldflags
+ `cat config.cc` ${CFLAGS} `cat config.cflags` LTunix.c \
+ ${LIBOBJ} `cat config.xobj` -o LTunix `cat config.ldflags`
+
+opt: ${CKTSTDB} ${OPTTST} FRC
+ @rm -f config.LT*
+ -@err=0; \
+ echo ""; \
+ echo "Optional tests:"; \
+ for i in ${OPTTST}; do \
+ ./$$i; \
+ if [ $$? -ne 0 ]; then \
+ err=`expr $$err + 1`; \
+ fi; \
+ done; \
+ if [ $$err -ne 0 ]; then \
+ echo "Failed tests: $$err"; \
+ else \
+ echo "All optional tests succeeded."; \
+ fi; \
+ echo "";
+ @rm -f config.LT*
+
+optional: opt
+
+silent: ${BASTST} ${STDTST} FRC
+ @rm -f config.LT*
+ @err=0; \
+ ./${BASTST} > /dev/null 2>&1; \
+ if [ $$? -ne 0 ]; then \
+ exit 1; \
+ fi; \
+ for i in ${STDTST}; do \
+ ./$$i > /dev/null 2>&1; \
+ if [ $$? -ne 0 ]; then \
+ err=`expr $$err + 1`; \
+ fi; \
+ done; \
+ rm -f config.LT*; \
+ if [ $$err -ne 0 ]; then \
+ exit 1; \
+ fi
+
+spotless: clean
+ rm -f config.*
+
+standard: all
+
+std: all
+
+test: all
diff --git a/tests/TestDB b/tests/TestDB
new file mode 100644
index 0000000..1ad618e
--- /dev/null
+++ b/tests/TestDB
@@ -0,0 +1,134 @@
+# TestDB -- lsof test suite data base
+#
+# This file contains the sorted words from config.cflags, less any leading "-D"
+# strings, joined on one line.
+#
+# See Add2TestDB for a script that will build a line for this file.
+#
+# $Id: TestDB,v 1.38 2013/01/02 17:03:58 abe Exp $
+
+LT_AIXA=0 LT_BIGF LT_CC LT_DIAL_aix LT_KMEM LT_VERS=4320
+LT_AIXA=0 LT_BIGF LT_CC LT_DIAL_aix LT_KMEM LT_VERS=4330
+LT_AIXA=1 LT_BIGF LT_CC LT_DIAL_aix LT_K64 LT_KMEM LT_VERS=5000
+LT_AIXA=0 LT_BIGF LT_CC LT_DIAL_aix LT_KMEM LT_VERS=5100
+LT_AIXA=0 LT_BIGF LT_DIAL_aix LT_GCC LT_KMEM LT_VERS=5100
+LT_AIXA=1 LT_BIGF LT_CC LT_DIAL_aix LT_K64 LT_KMEM LT_VERS=5100
+LT_AIXA=0 LT_BIGF LT_CC LT_DIAL_aix LT_KMEM LT_VERS=5200
+LT_AIXA=0 LT_BIGF LT_DIAL_aix LT_GCC LT_KMEM LT_VERS=5200
+LT_AIXA=1 LT_BIGF LT_CC LT_DIAL_aix LT_K64 LT_KMEM LT_VERS=5200
+LT_AIXA=1 LT_BIGF LT_DIAL_aix LT_GCC LT_K64 LT_KMEM LT_VERS=5200
+LT_AIXA=1 LT_BIGF LT_CC LT_DIAL_aix LT_K64 LT_KMEM LT_VERS=5300
+LT_BIGF LT_DIAL_bsdi LT_GCC LT_KMEM LT_VERS=40100
+LT_BIGF LT_DIAL_bsdi LT_GCC LT_KMEM LT_VERS=40300
+LT_CC LT_DIAL_darwin LT_KMEM LT_VERS=140
+LT_CC LT_DIAL_darwin LT_KMEM LT_VERS=530
+LT_CC LT_DIAL_darwin LT_KMEM LT_VERS=600
+LT_CC LT_DIAL_darwin LT_KMEM LT_VERS=700
+LT_CC LT_DIAL_darwin LT_KMEM LT_VERS=800
+LT_CC LT_DIAL_darwin LT_VERS=800
+LT_BIGF LT_CC LT_DIAL_darwin LT_KMEM LT_VERS=800
+LT_CC LT_DIAL_darwin LT_VERS=900
+LT_BIGF LT_CC LT_DIAL_darwin LT_VERS=900
+LT_BIGF LT_CC LT_DIAL_darwin LT_VERS=1000
+LT_BIGF LT_CC LT_DIAL_darwin LT_VERS=1100
+LT_BIGF LT_CC LT_DIAL_du LT_K64 LT_KMEM LT_VERS=40000
+LT_BIGF LT_CC LT_DIAL_du LT_K64 LT_KMEM LT_VERS=50000
+LT_BIGF LT_CC LT_DIAL_du LT_K64 LT_KMEM LT_VERS=50100
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4050
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4060
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4070
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4080
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4090
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4100
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=4110
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=5000
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=5010
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=5020
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=5030
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=5040
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=5050
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=6000
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=6010
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=6020
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=6040
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=7000
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=7010
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=7020
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=7030
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=7040
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=8000
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=8020
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=8030
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=9000
+LT_BIGF LT_CC LT_DIAL_freebsd LT_KMEM LT_VERS=10000
+LT_BIGF LT_CC LT_DIAL_hpux LT_KMEM LT_VERS=1020 _LARGEFILE64_SOURCE
+LT_BIGF LT_DIAL_hpux LT_GCC LT_KMEM LT_VERS=1020 _LARGEFILE64_SOURCE
+LT_BIGF LT_CC LT_DIAL_hpux LT_KMEM LT_VERS=1100 _LARGEFILE64_SOURCE
+LT_BIGF LT_CC LT_DIAL_hpux LT_K64 LT_KMEM LT_VERS=1100 _LARGEFILE64_SOURCE
+LT_BIGF LT_CC LT_DIAL_hpux LT_K64 LT_VERS=1111 _LARGEFILE64_SOURCE
+LT_BIGF LT_CC LT_DIAL_hpux LT_K64 LT_VERS=1123 _LARGEFILE64_SOURCE
+LT_BIGF LT_DIAL_hpux LT_GCC LT_K64 LT_VERS=1123 _LARGEFILE64_SOURCE
+LT_BIGF LT_CC LT_DIAL_hpux LT_K64 LT_VERS=1131 _LARGEFILE64_SOURCE
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24012 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24018 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24021 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24023 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24024 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24025 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24026 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24027 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24028 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24029 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=24030 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=26000 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=26018 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=26022 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=26032 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=26038 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_linux LT_VERS=31008 _FILE_OFFSET_BITS=64
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=1005000
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=1006000
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=2000000
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=2099009
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=2099010
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=2099011
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=2099012
+LT_BIGF LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=3099000
+LT_CC LT_DIAL_netbsd LT_KMEM LT_VERS=1040
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3000
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3010
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3020
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3030
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3040
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3050
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3060
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3070
+LT_BIGF LT_CC LT_DIAL_openbsd LT_KMEM LT_VERS=3090
+LT_CC LT_DIAL_osr LT_KMEM LT_VERS=504
+LT_CC LT_DIAL_osr LT_KMEM LT_VERS=506
+LT_DIAL_ns LT_GCC LT_KMEM LT_VERS=31
+LT_CC LT_DIAL_ns LT_KMEM LT_VERS=42
+LT_CC LT_DIAL_solaris LT_KMEM LT_VERS=20600
+LT_DIAL_solaris LT_GCC LT_KMEM LT_VERS=20600
+LT_BIGF LT_CC LT_DIAL_solaris LT_KMEM LT_VERS=70000
+LT_BIGF LT_CC LT_DIAL_solaris LT_K64 LT_KMEM LT_VERS=70000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_KMEM LT_VERS=70000
+LT_BIGF LT_CC LT_DIAL_solaris LT_KMEM LT_VERS=80000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_KMEM LT_VERS=80000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_K64 LT_KMEM LT_VERS=80000
+LT_BIGF LT_CC LT_DIAL_solaris LT_K64 LT_KMEM LT_VERS=80000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_KMEM LT_VERS=90000
+LT_BIGF LT_CC LT_DIAL_solaris LT_K64 LT_KMEM LT_VERS=90000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_K64 LT_KMEM LT_VERS=90000
+LT_BIGF LT_CC LT_DIAL_solaris LT_KMEM LT_VERS=100000
+LT_BIGF LT_CC LT_DIAL_solaris LT_K64 LT_KMEM LT_VERS=100000 LT_VPATH
+LT_BIGF LT_DIAL_solaris LT_GCC LT_KMEM LT_VERS=100000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_KMEM LT_VERS=100000 LT_VPATH
+LT_BIGF LT_CC LT_DIAL_solaris LT_K64 LT_KMEM LT_VERS=100000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_K64 LT_KMEM LT_VERS=100000
+LT_BIGF LT_DIAL_solaris LT_GCC LT_K64 LT_KMEM LT_VERS=100000 LT_VPATH
+LT_BIGF LT_CC LT_DIAL_solaris LT_K64 LT_KMEM LT_VERS=110000 LT_VPATH
+LT_BIGF LT_DIAL_solaris LT_GCC LT_K64 LT_KMEM LT_VERS=110000 LT_VPATH
+LT_BIGF LT_CC LT_DIAL_uw LT_KMEM LT_VERS=70101
+LT_BIGF LT_CC LT_DIAL_uw LT_KMEM LT_VERS=70103
+LT_BIGF LT_CC LT_DIAL_uw LT_KMEM LT_VERS=70104