diff options
author | Patrick McCarty <patrick.mccarty@linux.intel.com> | 2013-02-08 13:26:27 -0800 |
---|---|---|
committer | Patrick McCarty <patrick.mccarty@linux.intel.com> | 2013-02-08 13:26:27 -0800 |
commit | 9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7 (patch) | |
tree | 881eebfa461e4f8aa6b6f44b96ac0decd3bc887a /tests | |
download | lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.gz lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.bz2 lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.zip |
Imported Upstream version 4.87upstream/4.87
Diffstat (limited to 'tests')
-rw-r--r-- | tests/00README | 102 | ||||
-rwxr-xr-x | tests/Add2TestDB | 83 | ||||
-rwxr-xr-x | tests/CkTestDB | 136 | ||||
-rw-r--r-- | tests/LTbasic.c | 445 | ||||
-rw-r--r-- | tests/LTbigf.c | 767 | ||||
-rw-r--r-- | tests/LTdnlc.c | 426 | ||||
-rw-r--r-- | tests/LTlib.c | 1104 | ||||
-rw-r--r-- | tests/LTlock.c | 769 | ||||
-rw-r--r-- | tests/LTnfs.c | 531 | ||||
-rw-r--r-- | tests/LTnlink.c | 586 | ||||
-rw-r--r-- | tests/LTsock.c | 886 | ||||
-rw-r--r-- | tests/LTszoff.c | 509 | ||||
-rw-r--r-- | tests/LTunix.c | 364 | ||||
-rw-r--r-- | tests/LsofTest.h | 360 | ||||
-rw-r--r-- | tests/Makefile | 159 | ||||
-rw-r--r-- | tests/TestDB | 134 |
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 |