summaryrefslogtreecommitdiff
path: root/tests/LTlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/LTlib.c')
-rw-r--r--tests/LTlib.c1104
1 files changed, 1104 insertions, 0 deletions
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);
+}